예제 #1
0
    def wrap_aside(self, block, aside, view, frag, context):
        """
        Creates a div which identifies the aside, points to the original block,
        and writes out the json_init_args into a script tag.

        The default implementation creates a frag to wraps frag w/ a div identifying the xblock. If you have
        javascript, you'll need to override this impl
        """
        if not frag.content:
            return frag

        runtime_class = 'LmsRuntime'
        extra_data = {
            'block-id': quote_slashes(unicode(block.scope_ids.usage_id)),
            'course-id': quote_slashes(unicode(block.course_id)),
            'url-selector': 'asideBaseUrl',
            'runtime-class': runtime_class,
        }
        if self.request_token:
            extra_data['request-token'] = self.request_token

        return wrap_xblock_aside(
            runtime_class,
            aside,
            view,
            frag,
            context,
            usage_id_serializer=unicode,
            request_token=self.request_token,
            extra_data=extra_data,
        )
예제 #2
0
 def expected_handler_url(self, handler):
     """convenience method to get the reversed handler urls"""
     return "https://{}{}".format(settings.SITE_NAME, reverse(
         'xblock_handler_noauth',
         args=[
             text_type(self.course.id),
             quote_slashes(text_type(self.lti_published.scope_ids.usage_id).encode('utf-8')),
             handler
         ]
     ))
 def expected_handler_url(self, handler):
     """convenience method to get the reversed handler urls"""
     return "https://{}{}".format(settings.SITE_NAME, reverse(
         'courseware.module_render.handle_xblock_callback_noauth',
         args=[
             self.course.id.to_deprecated_string(),
             quote_slashes(unicode(self.lti_published.scope_ids.usage_id.to_deprecated_string()).encode('utf-8')),
             handler
         ]
     ))
예제 #4
0
 def expected_handler_url(self, handler):
     """convenience method to get the reversed handler urls"""
     return "https://{}{}".format(settings.SITE_NAME, reverse(
         'xblock_handler_noauth',
         args=[
             text_type(self.course.id),
             quote_slashes(text_type(self.lti_published.scope_ids.usage_id).encode('utf-8')),
             handler
         ]
     ))
예제 #5
0
def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
    """
    This method matches the signature for `xblock.runtime:Runtime.handler_url()`

    See :method:`xblock.runtime:Runtime.handler_url`
    """
    view_name = 'xblock_handler'
    if handler_name:
        # Be sure this is really a handler.
        #
        # We're checking the .__class__ instead of the block itself to avoid
        # auto-proxying from Descriptor -> Module, in case descriptors want
        # to ask for handler URLs without a student context.
        func = getattr(block.__class__, handler_name, None)
        if not func:
            raise ValueError(
                u"{!r} is not a function name".format(handler_name))

        # Is the following necessary? ProxyAttribute causes an UndefinedContext error
        # if trying this without the module system.
        #
        #if not getattr(func, "_is_xblock_handler", False):
        #    raise ValueError("{!r} is not a handler name".format(handler_name))

    if thirdparty:
        view_name = 'xblock_handler_noauth'

    url = reverse(view_name,
                  kwargs={
                      'course_id':
                      six.text_type(block.location.course_key),
                      'usage_id':
                      quote_slashes(six.text_type(block.scope_ids.usage_id)),
                      'handler':
                      handler_name,
                      'suffix':
                      suffix,
                  })

    # If suffix is an empty string, remove the trailing '/'
    if not suffix:
        url = url.rstrip('/')

    # If there is a query string, append it
    if query:
        url += '?' + query

    # If third-party, return fully-qualified url
    if thirdparty:
        scheme = "https" if settings.HTTPS == "on" else "http"
        url = '{scheme}://{host}{path}'.format(scheme=scheme,
                                               host=settings.SITE_NAME,
                                               path=url)

    return url
예제 #6
0
def _section_send_email(course, access):
    """ Provide data for the corresponding bulk email section """
    course_key = course.id

    # Monkey-patch applicable_aside_types to return no asides for the duration of this render
    with patch.object(course.runtime, 'applicable_aside_types',
                      null_applicable_aside_types):
        # This HtmlDescriptor is only being used to generate a nice text editor.
        html_module = HtmlDescriptor(
            course.system, DictFieldData({'data': ''}),
            ScopeIds(None, None, None,
                     course_key.make_usage_key('html', 'fake')))
        fragment = course.system.render(html_module, 'studio_view')
    fragment = wrap_xblock(
        'LmsRuntime',
        html_module,
        'studio_view',
        fragment,
        None,
        extra_data={"course-id": unicode(course_key)},
        usage_id_serializer=lambda usage_id: quote_slashes(unicode(usage_id)),
        # Generate a new request_token here at random, because this module isn't connected to any other
        # xblock rendering.
        request_token=uuid.uuid1().get_hex())
    cohorts = []
    if is_course_cohorted(course_key):
        cohorts = get_course_cohorts(course)
    email_editor = fragment.content
    section_data = {
        'section_key':
        'send_email',
        'section_display_name':
        _('Email'),
        'access':
        access,
        'send_email':
        reverse('send_email', kwargs={'course_id': unicode(course_key)}),
        'editor':
        email_editor,
        'cohorts':
        cohorts,
        'default_cohort_name':
        DEFAULT_COHORT_NAME,
        'list_instructor_tasks_url':
        reverse('list_instructor_tasks',
                kwargs={'course_id': unicode(course_key)}),
        'email_background_tasks_url':
        reverse('list_background_email_tasks',
                kwargs={'course_id': unicode(course_key)}),
        'email_content_history_url':
        reverse('list_email_content',
                kwargs={'course_id': unicode(course_key)}),
    }
    return section_data
 def get_handler_url(self, handler, xblock_name=None):
     """
     Get url for the specified xblock handler
     """
     if xblock_name is None:
         xblock_name = TestCrowdsourceHinter.XBLOCK_NAMES[0]
     return reverse('xblock_handler', kwargs={
         'course_id': str(self.course.id),
         'usage_id': quote_slashes(str(self.course.id.make_usage_key('crowdsourcehinter', xblock_name))),
         'handler': handler,
         'suffix': ''
     })
예제 #8
0
 def expected_handler_url(self, handler):
     """convenience method to get the reversed handler urls"""
     return "https://{}{}".format(
         settings.SITE_NAME,
         reverse('courseware.module_render.handle_xblock_callback_noauth',
                 args=[
                     self.course.id.to_deprecated_string(),
                     quote_slashes(
                         unicode(self.lti_published.scope_ids.usage_id.
                                 to_deprecated_string()).encode('utf-8')),
                     handler
                 ]))
예제 #9
0
 def get_handler_url(self, handler, xblock_name=None):
     """
     Get url for the specified xblock handler
     """
     if xblock_name is None:
         xblock_name = TestRecommender.XBLOCK_NAMES[0]
     return reverse('xblock_handler', kwargs={
         'course_id': text_type(self.course.id),
         'usage_id': quote_slashes(text_type(self.course.id.make_usage_key('recommender', xblock_name))),
         'handler': handler,
         'suffix': ''
     })
예제 #10
0
 def get_handler_url(self, handler, xblock_name=None):
     """
     Get url for the specified xblock handler
     """
     if xblock_name is None:
         xblock_name = TestRecommender.XBLOCK_NAMES[0]
     return reverse('xblock_handler', kwargs={
         'course_id': text_type(self.course.id),
         'usage_id': quote_slashes(text_type(self.course.id.make_usage_key('recommender', xblock_name))),
         'handler': handler,
         'suffix': ''
     })
def _section_send_email(course, access):
    """ Provide data for the corresponding bulk email section """
    course_key = course.id

    # Monkey-patch applicable_aside_types to return no asides for the duration of this render
    with patch.object(course.runtime, 'applicable_aside_types', null_applicable_aside_types):
        # This HtmlBlock is only being used to generate a nice text editor.
        html_module = HtmlBlock(
            course.system,
            DictFieldData({'data': ''}),
            ScopeIds(None, None, None, course_key.make_usage_key('html', 'fake'))
        )
        fragment = course.system.render(html_module, 'studio_view')
    fragment = wrap_xblock(
        'LmsRuntime', html_module, 'studio_view', fragment, None,
        extra_data={"course-id": str(course_key)},
        usage_id_serializer=lambda usage_id: quote_slashes(str(usage_id)),
        # Generate a new request_token here at random, because this module isn't connected to any other
        # xblock rendering.
        request_token=uuid.uuid1().hex
    )
    cohorts = []
    if is_course_cohorted(course_key):
        cohorts = get_course_cohorts(course)
    course_modes = []
    if not VerifiedTrackCohortedCourse.is_verified_track_cohort_enabled(course_key):
        course_modes = CourseMode.modes_for_course(course_key, include_expired=True, only_selectable=False)
    email_editor = fragment.content
    section_data = {
        'section_key': 'send_email',
        'section_display_name': _('Email'),
        'access': access,
        'send_email': reverse('send_email', kwargs={'course_id': str(course_key)}),
        'editor': email_editor,
        'cohorts': cohorts,
        'course_modes': course_modes,
        'default_cohort_name': DEFAULT_COHORT_NAME,
        'list_instructor_tasks_url': reverse(
            'list_instructor_tasks', kwargs={'course_id': str(course_key)}
        ),
        'email_background_tasks_url': reverse(
            'list_background_email_tasks', kwargs={'course_id': str(course_key)}
        ),
        'email_content_history_url': reverse(
            'list_email_content', kwargs={'course_id': str(course_key)}
        ),
    }
    if settings.FEATURES.get("ENABLE_NEW_BULK_EMAIL_EXPERIENCE", False) is not False:
        section_data[
            "communications_mfe_url"
        ] = f"{settings.COMMUNICATIONS_MICROFRONTEND_URL}/courses/{str(course_key)}/bulk_email"
    return section_data
 def get_handler_url(self, handler, xblock_name=None):
     """
     Get url for the specified xblock handler
     """
     if xblock_name is None:
         xblock_name = TestCrowdsourceHinter.XBLOCK_NAMES[0]
     return reverse('xblock_handler', kwargs={
         'course_id': self.course.id.to_deprecated_string(),
         'usage_id': quote_slashes(self.course.id.make_usage_key('crowdsourcehinter', xblock_name).
                                   to_deprecated_string()),
         'handler': handler,
         'suffix': ''
     })
예제 #13
0
def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
    """
    This method matches the signature for `xblock.runtime:Runtime.handler_url()`

    See :method:`xblock.runtime:Runtime.handler_url`
    """
    view_name = 'xblock_handler'
    if handler_name:
        # Be sure this is really a handler.
        #
        # We're checking the .__class__ instead of the block itself to avoid
        # auto-proxying from Descriptor -> Module, in case descriptors want
        # to ask for handler URLs without a student context.
        func = getattr(block.__class__, handler_name, None)
        if not func:
            raise ValueError("{!r} is not a function name".format(handler_name))

        # Is the following necessary? ProxyAttribute causes an UndefinedContext error
        # if trying this without the module system.
        #
        #if not getattr(func, "_is_xblock_handler", False):
        #    raise ValueError("{!r} is not a handler name".format(handler_name))

    if thirdparty:
        view_name = 'xblock_handler_noauth'

    url = reverse(view_name, kwargs={
        'course_id': unicode(block.location.course_key),
        'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')),
        'handler': handler_name,
        'suffix': suffix,
    })

    # If suffix is an empty string, remove the trailing '/'
    if not suffix:
        url = url.rstrip('/')

    # If there is a query string, append it
    if query:
        url += '?' + query

    # If third-party, return fully-qualified url
    if thirdparty:
        scheme = "https" if settings.HTTPS == "on" else "http"
        url = '{scheme}://{host}{path}'.format(
            scheme=scheme,
            host=settings.SITE_NAME,
            path=url
        )

    return url
예제 #14
0
def _section_send_email(course, access):
    """ Provide data for the corresponding bulk email section """
    course_key = course.id

    # Monkey-patch applicable_aside_types to return no asides for the duration of this render
    with patch.object(course.runtime, 'applicable_aside_types', null_applicable_aside_types):
        # This HtmlDescriptor is only being used to generate a nice text editor.
        html_module = HtmlDescriptor(
            course.system,
            DictFieldData({'data': ''}),
            ScopeIds(None, None, None, course_key.make_usage_key('html', 'fake'))
        )
        fragment = course.system.render(html_module, 'studio_view')
    fragment = wrap_xblock(
        'LmsRuntime', html_module, 'studio_view', fragment, None,
        extra_data={"course-id": unicode(course_key)},
        usage_id_serializer=lambda usage_id: quote_slashes(unicode(usage_id)),
        # Generate a new request_token here at random, because this module isn't connected to any other
        # xblock rendering.
        request_token=uuid.uuid1().get_hex()
    )
    cohorts = []
    if is_course_cohorted(course_key):
        cohorts = get_course_cohorts(course)
    course_modes = []
    if not VerifiedTrackCohortedCourse.is_verified_track_cohort_enabled(course_key):
        course_modes = CourseMode.modes_for_course(course_key, include_expired=True, only_selectable=False)
    email_editor = fragment.content
    section_data = {
        'section_key': 'send_email',
        'section_display_name': _('Email'),
        'access': access,
        'send_email': reverse('send_email', kwargs={'course_id': unicode(course_key)}),
        'editor': email_editor,
        'cohorts': cohorts,
        'course_modes': course_modes,
        'default_cohort_name': DEFAULT_COHORT_NAME,
        'list_instructor_tasks_url': reverse(
            'list_instructor_tasks', kwargs={'course_id': unicode(course_key)}
        ),
        'email_background_tasks_url': reverse(
            'list_background_email_tasks', kwargs={'course_id': unicode(course_key)}
        ),
        'email_content_history_url': reverse(
            'list_email_content', kwargs={'course_id': unicode(course_key)}
        ),
    }
    from openedx.stanford.lms.djangoapps.instructor.views.instructor_dashboard import send_email_section_data
    section_data.update(send_email_section_data())
    return section_data
예제 #15
0
def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
    """
    This method matches the signature for `xblock.runtime:Runtime.handler_url()`

    :param block: The block to generate the url for
    :param handler_name: The handler on that block that the url should resolve to
    :param suffix: Any path suffix that should be added to the handler url
    :param query: Any query string that should be added to the handler url
        (which should not include an initial ? or &)
    :param thirdparty: If true, return a fully-qualified URL instead of relative
        URL. This is useful for URLs to be used by third-party services.
    """
    view_name = 'xblock_handler'
    if handler_name:
        # Be sure this is really a handler.
        #
        # We're checking the .__class__ instead of the block itself to avoid
        # auto-proxying from Descriptor -> Module, in case descriptors want
        # to ask for handler URLs without a student context.
        func = getattr(block.__class__, handler_name, None)
        if not func:
            raise ValueError(f"{handler_name!r} is not a function name")

    if thirdparty:
        view_name = 'xblock_handler_noauth'

    url = reverse(view_name,
                  kwargs={
                      'course_id': str(block.location.course_key),
                      'usage_id': quote_slashes(str(block.scope_ids.usage_id)),
                      'handler': handler_name,
                      'suffix': suffix,
                  })

    # If suffix is an empty string, remove the trailing '/'
    if not suffix:
        url = url.rstrip('/')

    # If there is a query string, append it
    if query:
        url += '?' + query

    # If third-party, return fully-qualified url
    if thirdparty:
        scheme = "https" if settings.HTTPS == "on" else "http"
        url = '{scheme}://{host}{path}'.format(scheme=scheme,
                                               host=settings.SITE_NAME,
                                               path=url)

    return url
예제 #16
0
 def render_problem(self, username, problem_url_name):
     """
     Use ajax interface to request html for a problem.
     """
     # make sure that the requested user is logged in, so that the ajax call works
     # on the right problem:
     self.login_username(username)
     # make ajax call:
     modx_url = reverse('xblock_handler', kwargs={
         'course_id': self.course.id.to_deprecated_string(),
         'usage_id': quote_slashes(InstructorTaskModuleTestCase.problem_location(problem_url_name).to_deprecated_string()),
         'handler': 'xmodule_handler',
         'suffix': 'problem_get',
     })
     resp = self.client.post(modx_url, {})
     return resp
예제 #17
0
 def render_problem(self, username, problem_url_name):
     """
     Use ajax interface to request html for a problem.
     """
     # make sure that the requested user is logged in, so that the ajax call works
     # on the right problem:
     self.login_username(username)
     # make ajax call:
     modx_url = reverse('xblock_handler', kwargs={
         'course_id': text_type(self.course.id),
         'usage_id': quote_slashes(text_type(InstructorTaskModuleTestCase.problem_location(problem_url_name))),
         'handler': 'xmodule_handler',
         'suffix': 'problem_get',
     })
     resp = self.client.post(modx_url, {})
     return resp
    def modx_url(self, problem_location, dispatch):
        """
        Return the url needed for the desired action.

        problem_location: location of the problem on which we want some action

        dispatch: the the action string that gets passed to the view as a kwarg
            example: 'check_problem' for having responses processed
        """
        return reverse('xblock_handler',
                       kwargs={
                           'course_id': text_type(self.course.id),
                           'usage_id':
                           quote_slashes(text_type(problem_location)),
                           'handler': 'xmodule_handler',
                           'suffix': dispatch,
                       })
    def modx_url(self, problem_location, dispatch):
        """
        Return the url needed for the desired action.

        problem_location: location of the problem on which we want some action

        dispatch: the the action string that gets passed to the view as a kwarg
            example: 'check_problem' for having responses processed
        """
        return reverse(
            'xblock_handler',
            kwargs={
                'course_id': text_type(self.course.id),
                'usage_id': quote_slashes(text_type(problem_location)),
                'handler': 'xmodule_handler',
                'suffix': dispatch,
            }
        )
예제 #20
0
    def submit_student_answer(self, username, problem_url_name, responses):
        """
        Use ajax interface to submit a student answer.

        Assumes the input list of responses has two values.
        """
        def get_input_id(response_id):
            """Creates input id using information about the test course and the current problem."""
            # Note that this is a capa-specific convention.  The form is a version of the problem's
            # URL, modified so that it can be easily stored in html, prepended with "input-" and
            # appended with a sequence identifier for the particular response the input goes to.
            course_key = self.course.id
            return u'input_i4x-{0}-{1}-problem-{2}_{3}'.format(
                course_key.org.replace(u'.', u'_'),
                course_key.course.replace(u'.', u'_'), problem_url_name,
                response_id)

        # make sure that the requested user is logged in, so that the ajax call works
        # on the right problem:
        self.login_username(username)
        # make ajax call:
        modx_url = reverse(
            'xblock_handler',
            kwargs={
                'course_id':
                text_type(self.course.id),
                'usage_id':
                quote_slashes(
                    text_type(
                        InstructorTaskModuleTestCase.problem_location(
                            problem_url_name, self.course.id))),
                'handler':
                'xmodule_handler',
                'suffix':
                'problem_check',
            })

        # assign correct identifier to each response.
        resp = self.client.post(
            modx_url, {
                get_input_id(u'{}_1').format(index): response
                for index, response in enumerate(responses, 2)
            })
        return resp
예제 #21
0
 def test_xblock_handlers(self, xblock_type, xblock_name, user, status_code):
     """
     Test the ajax calls to the problem xblock to ensure the LMS is sending back
     the expected response codes on requests when content is gated for audit users
     (404) and when it is available to audit users (200). Content is always available
     to verified users.
     """
     problem_location = self.blocks_dict[xblock_name].scope_ids.usage_id
     url = reverse(
         'xblock_handler',
         kwargs={
             'course_id': six.text_type(self.course.id),
             'usage_id': quote_slashes(six.text_type(problem_location)),
             'handler': 'xmodule_handler',
             'suffix': 'problem_show',
         }
     )
     self.client.login(username=self.users[user].username, password=TEST_PASSWORD)
     response = self.client.post(url)
     self.assertEqual(response.status_code, status_code)
예제 #22
0
 def test_xblock_handlers(self, xblock_type, xblock_name, user, status_code):
     """
     Test the ajax calls to the problem xblock to ensure the LMS is sending back
     the expected response codes on requests when content is gated for audit users
     (404) and when it is available to audit users (200). Content is always available
     to verified users.
     """
     problem_location = self.blocks_dict[xblock_name].scope_ids.usage_id
     url = reverse(
         'xblock_handler',
         kwargs={
             'course_id': six.text_type(self.course.id),
             'usage_id': quote_slashes(six.text_type(problem_location)),
             'handler': 'xmodule_handler',
             'suffix': 'problem_show',
         }
     )
     self.client.login(username=self.users[user].username, password=TEST_PASSWORD)
     response = self.client.post(url)
     self.assertEqual(response.status_code, status_code)
예제 #23
0
 def test_wrap_xblock(self, course_id, data_usage_id):
     """
     Verify that new content is added and the resources are the same.
     """
     fragment = self.create_fragment(u"<h1>Test!</h1>")
     course = getattr(self, course_id)
     test_wrap_output = wrap_xblock(
         runtime_class='TestRuntime',
         block=course,
         view='baseview',
         frag=fragment,
         context=None,
         usage_id_serializer=lambda usage_id: quote_slashes(unicode(usage_id)),
         request_token=uuid.uuid1().get_hex()
     )
     self.assertIsInstance(test_wrap_output, Fragment)
     self.assertIn('xblock-baseview', test_wrap_output.content)
     self.assertIn('data-runtime-class="TestRuntime"', test_wrap_output.content)
     self.assertIn(data_usage_id, test_wrap_output.content)
     self.assertIn('<h1>Test!</h1>', test_wrap_output.content)
     self.assertEqual(test_wrap_output.resources[0].data, u'body {background-color:red;}')
     self.assertEqual(test_wrap_output.resources[1].data, 'alert("Hi!");')
예제 #24
0
    def wrap_aside(self, block, aside, view, frag, context):
        """
        Creates a div which identifies the aside, points to the original block,
        and writes out the json_init_args into a script tag.

        The default implementation creates a frag to wraps frag w/ a div identifying the xblock. If you have
        javascript, you'll need to override this impl
        """
        extra_data = {
            'block-id': quote_slashes(unicode(block.scope_ids.usage_id)),
            'url-selector': 'asideBaseUrl',
            'runtime-class': 'LmsRuntime',
        }
        if self.request_token:
            extra_data['request-token'] = self.request_token

        return self._wrap_ele(
            aside,
            view,
            frag,
            extra_data,
        )
 def test_wrap_xblock(self, course_id, data_usage_id):
     """
     Verify that new content is added and the resources are the same.
     """
     fragment = self.create_fragment(u"<h1>Test!</h1>")
     course = getattr(self, course_id)
     test_wrap_output = wrap_xblock(runtime_class='TestRuntime',
                                    block=course,
                                    view='baseview',
                                    frag=fragment,
                                    context=None,
                                    usage_id_serializer=lambda usage_id:
                                    quote_slashes(unicode(usage_id)),
                                    request_token=uuid.uuid1().get_hex())
     self.assertIsInstance(test_wrap_output, Fragment)
     self.assertIn('xblock-baseview', test_wrap_output.content)
     self.assertIn('data-runtime-class="TestRuntime"',
                   test_wrap_output.content)
     self.assertIn(data_usage_id, test_wrap_output.content)
     self.assertIn('<h1>Test!</h1>', test_wrap_output.content)
     self.assertEqual(test_wrap_output.resources[0].data,
                      u'body {background-color:red;}')
     self.assertEqual(test_wrap_output.resources[1].data, 'alert("Hi!");')
예제 #26
0
    def submit_student_answer(self, username, problem_url_name, responses):
        """
        Use ajax interface to submit a student answer.

        Assumes the input list of responses has two values.
        """
        def get_input_id(response_id):
            """Creates input id using information about the test course and the current problem."""
            # Note that this is a capa-specific convention.  The form is a version of the problem's
            # URL, modified so that it can be easily stored in html, prepended with "input-" and
            # appended with a sequence identifier for the particular response the input goes to.
            course_key = self.course.id
            return u'input_i4x-{0}-{1}-problem-{2}_{3}'.format(
                course_key.org.replace(u'.', u'_'),
                course_key.course.replace(u'.', u'_'),
                problem_url_name,
                response_id
            )

        # make sure that the requested user is logged in, so that the ajax call works
        # on the right problem:
        self.login_username(username)
        # make ajax call:
        modx_url = reverse('xblock_handler', kwargs={
            'course_id': text_type(self.course.id),
            'usage_id': quote_slashes(
                text_type(InstructorTaskModuleTestCase.problem_location(problem_url_name, self.course.id))
            ),
            'handler': 'xmodule_handler',
            'suffix': 'problem_check',
        })

        # assign correct identifier to each response.
        resp = self.client.post(modx_url, {
            get_input_id(u'{}_1').format(index): response for index, response in enumerate(responses, 2)
        })
        return resp
예제 #27
0
 def test_wrap_xblock(self, course_id, data_usage_id):
     """
     Verify that new content is added and the resources are the same.
     """
     fragment = self.create_fragment("<h1>Test!</h1>")
     fragment.initialize_js('BlockMain')  # wrap_block() sets some attributes only if there is JS.
     course = getattr(self, course_id)
     test_wrap_output = wrap_xblock(
         runtime_class='TestRuntime',
         block=course,
         view='baseview',
         frag=fragment,
         context={"wrap_xblock_data": {"custom-attribute": "custom-value"}},
         usage_id_serializer=lambda usage_id: quote_slashes(str(usage_id)),
         request_token=uuid.uuid1().hex
     )
     assert isinstance(test_wrap_output, Fragment)
     assert 'xblock-baseview' in test_wrap_output.content
     assert 'data-runtime-class="TestRuntime"' in test_wrap_output.content
     assert data_usage_id in test_wrap_output.content
     assert '<h1>Test!</h1>' in test_wrap_output.content
     assert 'data-custom-attribute="custom-value"' in test_wrap_output.content
     assert test_wrap_output.resources[0].data == 'body {background-color:red;}'
     assert test_wrap_output.resources[1].data == 'alert("Hi!");'
예제 #28
0
 def test_inverse(self, test_string):
     self.assertEquals(test_string, unquote_slashes(quote_slashes(test_string)))
예제 #29
0
def get_module_system_for_user(
        user,
        student_data,  # TODO
        # Arguments preceding this comment have user binding, those following don't
        descriptor,
        course_id,
        track_function,
        xqueue_callback_url_prefix,
        request_token,
        position=None,
        wrap_xmodule_display=True,
        grade_bucket_type=None,
        static_asset_path='',
        user_location=None,
        disable_staff_debug_info=False,
        course=None
):
    """
    Helper function that returns a module system and student_data bound to a user and a descriptor.

    The purpose of this function is to factor out everywhere a user is implicitly bound when creating a module,
    to allow an existing module to be re-bound to a user.  Most of the user bindings happen when creating the
    closures that feed the instantiation of ModuleSystem.

    The arguments fall into two categories: those that have explicit or implicit user binding, which are user
    and student_data, and those don't and are just present so that ModuleSystem can be instantiated, which
    are all the other arguments.  Ultimately, this isn't too different than how get_module_for_descriptor_internal
    was before refactoring.

    Arguments:
        see arguments for get_module()
        request_token (str): A token unique to the request use by xblock initialization

    Returns:
        (LmsModuleSystem, KvsFieldData):  (module system, student_data) bound to, primarily, the user and descriptor
    """

    def make_xqueue_callback(dispatch='score_update'):
        """
        Returns fully qualified callback URL for external queueing system
        """
        relative_xqueue_callback_url = reverse(
            'xqueue_callback',
            kwargs=dict(
                course_id=text_type(course_id),
                userid=str(user.id),
                mod_id=text_type(descriptor.location),
                dispatch=dispatch
            ),
        )
        return xqueue_callback_url_prefix + relative_xqueue_callback_url

    # Default queuename is course-specific and is derived from the course that
    #   contains the current module.
    # TODO: Queuename should be derived from 'course_settings.json' of each course
    xqueue_default_queuename = descriptor.location.org + '-' + descriptor.location.course

    xqueue = {
        'interface': XQUEUE_INTERFACE,
        'construct_callback': make_xqueue_callback,
        'default_queuename': xqueue_default_queuename.replace(' ', '_'),
        'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS
    }

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor_internal() with all values except `descriptor` set.

        Because it does an access check, it may return None.
        """
        # TODO: fix this so that make_xqueue_callback uses the descriptor passed into
        # inner_get_module, not the parent's callback.  Add it as an argument....
        return get_module_for_descriptor_internal(
            user=user,
            descriptor=descriptor,
            student_data=student_data,
            course_id=course_id,
            track_function=track_function,
            xqueue_callback_url_prefix=xqueue_callback_url_prefix,
            position=position,
            wrap_xmodule_display=wrap_xmodule_display,
            grade_bucket_type=grade_bucket_type,
            static_asset_path=static_asset_path,
            user_location=user_location,
            request_token=request_token,
            course=course
        )

    def get_event_handler(event_type):
        """
        Return an appropriate function to handle the event.

        Returns None if no special processing is required.
        """
        handlers = {
            'grade': handle_grade_event,
        }
        if completion_waffle.waffle().is_enabled(completion_waffle.ENABLE_COMPLETION_TRACKING):
            handlers.update({
                'completion': handle_completion_event,
                'progress': handle_deprecated_progress_event,
            })
        return handlers.get(event_type)

    def publish(block, event_type, event):
        """
        A function that allows XModules to publish events.
        """
        handle_event = get_event_handler(event_type)
        if handle_event and not is_masquerading_as_specific_student(user, course_id):
            handle_event(block, event)
        else:
            context = contexts.course_context_from_course_id(course_id)
            if block.runtime.user_id:
                context['user_id'] = block.runtime.user_id
            context['asides'] = {}
            for aside in block.runtime.get_asides(block):
                if hasattr(aside, 'get_event_context'):
                    aside_event_info = aside.get_event_context(event_type, event)
                    if aside_event_info is not None:
                        context['asides'][aside.scope_ids.block_type] = aside_event_info
            with tracker.get_tracker().context(event_type, context):
                track_function(event_type, event)

    def handle_completion_event(block, event):
        """
        Submit a completion object for the block.
        """
        if not completion_waffle.waffle().is_enabled(completion_waffle.ENABLE_COMPLETION_TRACKING):
            raise Http404
        else:
            BlockCompletion.objects.submit_completion(
                user=user,
                course_key=course_id,
                block_key=block.scope_ids.usage_id,
                completion=event['completion'],
            )

    def handle_grade_event(block, event):
        """
        Submit a grade for the block.
        """
        SCORE_PUBLISHED.send(
            sender=None,
            block=block,
            user=user,
            raw_earned=event['value'],
            raw_possible=event['max_value'],
            only_if_higher=event.get('only_if_higher'),
            score_deleted=event.get('score_deleted'),
            grader_response=event.get('grader_response')
        )

    def handle_deprecated_progress_event(block, event):
        """
        DEPRECATED: Submit a completion for the block represented by the
        progress event.

        This exists to support the legacy progress extension used by
        edx-solutions.  New XBlocks should not emit these events, but instead
        emit completion events directly.
        """
        if not completion_waffle.waffle().is_enabled(completion_waffle.ENABLE_COMPLETION_TRACKING):
            raise Http404
        else:
            requested_user_id = event.get('user_id', user.id)
            if requested_user_id != user.id:
                log.warning("{} tried to submit a completion on behalf of {}".format(user, requested_user_id))
                return

            # If blocks explicitly declare support for the new completion API,
            # we expect them to emit 'completion' events,
            # and we ignore the deprecated 'progress' events
            # in order to avoid duplicate work and possibly conflicting semantics.
            if not getattr(block, 'has_custom_completion', False):
                BlockCompletion.objects.submit_completion(
                    user=user,
                    course_key=course_id,
                    block_key=block.scope_ids.usage_id,
                    completion=1.0,
                )

    def rebind_noauth_module_to_user(module, real_user):
        """
        A function that allows a module to get re-bound to a real user if it was previously bound to an AnonymousUser.

        Will only work within a module bound to an AnonymousUser, e.g. one that's instantiated by the noauth_handler.

        Arguments:
            module (any xblock type):  the module to rebind
            real_user (django.contrib.auth.models.User):  the user to bind to

        Returns:
            nothing (but the side effect is that module is re-bound to real_user)
        """
        if user.is_authenticated:
            err_msg = ("rebind_noauth_module_to_user can only be called from a module bound to "
                       "an anonymous user")
            log.error(err_msg)
            raise LmsModuleRenderError(err_msg)

        field_data_cache_real_user = FieldDataCache.cache_for_descriptor_descendents(
            course_id,
            real_user,
            module.descriptor,
            asides=XBlockAsidesConfig.possible_asides(),
        )
        student_data_real_user = KvsFieldData(DjangoKeyValueStore(field_data_cache_real_user))

        (inner_system, inner_student_data) = get_module_system_for_user(
            user=real_user,
            student_data=student_data_real_user,  # These have implicit user bindings, rest of args considered not to
            descriptor=module.descriptor,
            course_id=course_id,
            track_function=track_function,
            xqueue_callback_url_prefix=xqueue_callback_url_prefix,
            position=position,
            wrap_xmodule_display=wrap_xmodule_display,
            grade_bucket_type=grade_bucket_type,
            static_asset_path=static_asset_path,
            user_location=user_location,
            request_token=request_token,
            course=course
        )

        module.descriptor.bind_for_student(
            inner_system,
            real_user.id,
            [
                partial(OverrideFieldData.wrap, real_user, course),
                partial(LmsFieldData, student_data=inner_student_data),
            ],
        )

        module.descriptor.scope_ids = (
            module.descriptor.scope_ids._replace(user_id=real_user.id)
        )
        module.scope_ids = module.descriptor.scope_ids  # this is needed b/c NamedTuples are immutable
        # now bind the module to the new ModuleSystem instance and vice-versa
        module.runtime = inner_system
        inner_system.xmodule_instance = module

    # Build a list of wrapping functions that will be applied in order
    # to the Fragment content coming out of the xblocks that are about to be rendered.
    block_wrappers = []

    if is_masquerading_as_specific_student(user, course_id):
        block_wrappers.append(filter_displayed_blocks)

    if settings.FEATURES.get("LICENSING", False):
        block_wrappers.append(wrap_with_license)

    # Wrap the output display in a single div to allow for the XModule
    # javascript to be bound correctly
    if wrap_xmodule_display is True:
        block_wrappers.append(partial(
            wrap_xblock,
            'LmsRuntime',
            extra_data={'course-id': text_type(course_id)},
            usage_id_serializer=lambda usage_id: quote_slashes(text_type(usage_id)),
            request_token=request_token,
        ))

    # TODO (cpennington): When modules are shared between courses, the static
    # prefix is going to have to be specific to the module, not the directory
    # that the xml was loaded from

    # Rewrite urls beginning in /static to point to course-specific content
    block_wrappers.append(partial(
        replace_static_urls,
        getattr(descriptor, 'data_dir', None),
        course_id=course_id,
        static_asset_path=static_asset_path or descriptor.static_asset_path
    ))

    # Allow URLs of the form '/course/' refer to the root of multicourse directory
    #   hierarchy of this course
    block_wrappers.append(partial(replace_course_urls, course_id))

    # this will rewrite intra-courseware links (/jump_to_id/<id>). This format
    # is an improvement over the /course/... format for studio authored courses,
    # because it is agnostic to course-hierarchy.
    # NOTE: module_id is empty string here. The 'module_id' will get assigned in the replacement
    # function, we just need to specify something to get the reverse() to work.
    block_wrappers.append(partial(
        replace_jump_to_id_urls,
        course_id,
        reverse('jump_to_id', kwargs={'course_id': text_type(course_id), 'module_id': ''}),
    ))

    if settings.FEATURES.get('DISPLAY_DEBUG_INFO_TO_STAFF'):
        if is_masquerading_as_specific_student(user, course_id):
            # When masquerading as a specific student, we want to show the debug button
            # unconditionally to enable resetting the state of the student we are masquerading as.
            # We already know the user has staff access when masquerading is active.
            staff_access = True
            # To figure out whether the user has instructor access, we temporarily remove the
            # masquerade_settings from the real_user.  With the masquerading settings in place,
            # the result would always be "False".
            masquerade_settings = user.real_user.masquerade_settings
            del user.real_user.masquerade_settings
            user.real_user.masquerade_settings = masquerade_settings
        else:
            staff_access = has_access(user, 'staff', descriptor, course_id)
        if staff_access:
            block_wrappers.append(partial(add_staff_markup, user, disable_staff_debug_info))

    # These modules store data using the anonymous_student_id as a key.
    # To prevent loss of data, we will continue to provide old modules with
    # the per-student anonymized id (as we have in the past),
    # while giving selected modules a per-course anonymized id.
    # As we have the time to manually test more modules, we can add to the list
    # of modules that get the per-course anonymized id.
    is_pure_xblock = isinstance(descriptor, XBlock) and not isinstance(descriptor, XModuleDescriptor)
    module_class = getattr(descriptor, 'module_class', None)
    is_lti_module = not is_pure_xblock and issubclass(module_class, LTIModule)
    if is_pure_xblock or is_lti_module:
        anonymous_student_id = anonymous_id_for_user(user, course_id)
    else:
        anonymous_student_id = anonymous_id_for_user(user, None)

    field_data = LmsFieldData(descriptor._field_data, student_data)  # pylint: disable=protected-access

    user_is_staff = bool(has_access(user, u'staff', descriptor.location, course_id))

    system = LmsModuleSystem(
        track_function=track_function,
        render_template=render_to_string,
        static_url=settings.STATIC_URL,
        xqueue=xqueue,
        # TODO (cpennington): Figure out how to share info between systems
        filestore=descriptor.runtime.resources_fs,
        get_module=inner_get_module,
        user=user,
        debug=settings.DEBUG,
        hostname=settings.SITE_NAME,
        # TODO (cpennington): This should be removed when all html from
        # a module is coming through get_html and is therefore covered
        # by the replace_static_urls code below
        replace_urls=partial(
            static_replace.replace_static_urls,
            data_directory=getattr(descriptor, 'data_dir', None),
            course_id=course_id,
            static_asset_path=static_asset_path or descriptor.static_asset_path,
        ),
        replace_course_urls=partial(
            static_replace.replace_course_urls,
            course_key=course_id
        ),
        replace_jump_to_id_urls=partial(
            static_replace.replace_jump_to_id_urls,
            course_id=course_id,
            jump_to_id_base_url=reverse('jump_to_id', kwargs={'course_id': text_type(course_id), 'module_id': ''})
        ),
        node_path=settings.NODE_PATH,
        publish=publish,
        anonymous_student_id=anonymous_student_id,
        course_id=course_id,
        cache=cache,
        can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)),
        get_python_lib_zip=(lambda: get_python_lib_zip(contentstore, course_id)),
        # TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington)
        mixins=descriptor.runtime.mixologist._mixins,  # pylint: disable=protected-access
        wrappers=block_wrappers,
        get_real_user=user_by_anonymous_id,
        services={
            'fs': FSService(),
            'field-data': field_data,
            'user': DjangoXBlockUserService(user, user_is_staff=user_is_staff),
            'verification': XBlockVerificationService(),
            'proctoring': ProctoringService(),
            'milestones': milestones_helpers.get_service(),
            'credit': CreditService(),
            'bookmarks': BookmarksService(user=user),
            'gating': GatingService(),
        },
        get_user_role=lambda: get_user_role(user, course_id),
        descriptor_runtime=descriptor._runtime,  # pylint: disable=protected-access
        rebind_noauth_module_to_user=rebind_noauth_module_to_user,
        user_location=user_location,
        request_token=request_token,
    )

    # pass position specified in URL to module through ModuleSystem
    if position is not None:
        try:
            position = int(position)
        except (ValueError, TypeError):
            log.exception('Non-integer %r passed as position.', position)
            position = None

    system.set('position', position)

    system.set(u'user_is_staff', user_is_staff)
    system.set(u'user_is_admin', bool(has_access(user, u'staff', 'global')))
    system.set(u'user_is_beta_tester', CourseBetaTesterRole(course_id).has_user(user))
    system.set(u'days_early_for_beta', descriptor.days_early_for_beta)

    # make an ErrorDescriptor -- assuming that the descriptor's system is ok
    if has_access(user, u'staff', descriptor.location, course_id):
        system.error_descriptor_class = ErrorDescriptor
    else:
        system.error_descriptor_class = NonStaffErrorDescriptor

    return system, field_data
예제 #30
0
 def test_escaped(self, test_string):
     self.assertNotIn('/', quote_slashes(test_string))
 def test_escaped(self, test_string):
     assert '/' not in quote_slashes(test_string)
예제 #32
0
 def get_url(self, dispatch):
     """Return item url with dispatch."""
     return reverse(
         'xblock_handler',
         args=(unicode(self.course.id), quote_slashes(self.item_url), 'xmodule_handler', dispatch)
     )
예제 #33
0
 def get_url(self, dispatch):
     """Return item url with dispatch."""
     return reverse('xblock_handler',
                    args=(unicode(self.course.id),
                          quote_slashes(self.item_url), 'xmodule_handler',
                          dispatch))
예제 #34
0
 def test_escaped(self, test_string):
     self.assertNotIn('/', quote_slashes(test_string))
예제 #35
0
def get_module_system_for_user(
        user,
        student_data,  # TODO  # pylint: disable=too-many-statements
        # Arguments preceding this comment have user binding, those following don't
        descriptor,
        course_id,
        track_function,
        xqueue_callback_url_prefix,
        request_token,
        position=None,
        wrap_xmodule_display=True,
        grade_bucket_type=None,
        static_asset_path='',
        user_location=None,
        disable_staff_debug_info=False,
        course=None
):
    """
    Helper function that returns a module system and student_data bound to a user and a descriptor.

    The purpose of this function is to factor out everywhere a user is implicitly bound when creating a module,
    to allow an existing module to be re-bound to a user.  Most of the user bindings happen when creating the
    closures that feed the instantiation of ModuleSystem.

    The arguments fall into two categories: those that have explicit or implicit user binding, which are user
    and student_data, and those don't and are just present so that ModuleSystem can be instantiated, which
    are all the other arguments.  Ultimately, this isn't too different than how get_module_for_descriptor_internal
    was before refactoring.

    Arguments:
        see arguments for get_module()
        request_token (str): A token unique to the request use by xblock initialization

    Returns:
        (LmsModuleSystem, KvsFieldData):  (module system, student_data) bound to, primarily, the user and descriptor
    """

    def make_xqueue_callback(dispatch='score_update'):
        """
        Returns fully qualified callback URL for external queueing system
        """
        relative_xqueue_callback_url = reverse(
            'xqueue_callback',
            kwargs=dict(
                course_id=course_id.to_deprecated_string(),
                userid=str(user.id),
                mod_id=descriptor.location.to_deprecated_string(),
                dispatch=dispatch
            ),
        )
        return xqueue_callback_url_prefix + relative_xqueue_callback_url

    # Default queuename is course-specific and is derived from the course that
    #   contains the current module.
    # TODO: Queuename should be derived from 'course_settings.json' of each course
    xqueue_default_queuename = descriptor.location.org + '-' + descriptor.location.course

    xqueue = {
        'interface': XQUEUE_INTERFACE,
        'construct_callback': make_xqueue_callback,
        'default_queuename': xqueue_default_queuename.replace(' ', '_'),
        'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS
    }

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor_internal() with all values except `descriptor` set.

        Because it does an access check, it may return None.
        """
        # TODO: fix this so that make_xqueue_callback uses the descriptor passed into
        # inner_get_module, not the parent's callback.  Add it as an argument....
        return get_module_for_descriptor_internal(
            user=user,
            descriptor=descriptor,
            student_data=student_data,
            course_id=course_id,
            track_function=track_function,
            xqueue_callback_url_prefix=xqueue_callback_url_prefix,
            position=position,
            wrap_xmodule_display=wrap_xmodule_display,
            grade_bucket_type=grade_bucket_type,
            static_asset_path=static_asset_path,
            user_location=user_location,
            request_token=request_token,
            course=course
        )

    def get_event_handler(event_type):
        """
        Return an appropriate function to handle the event.

        Returns None if no special processing is required.
        """
        handlers = {
            'completion': handle_completion_event,
            'grade': handle_grade_event,
            'progress': handle_deprecated_progress_event,
        }
        return handlers.get(event_type)

    def publish(block, event_type, event):
        """
        A function that allows XModules to publish events.
        """
        handle_event = get_event_handler(event_type)
        if handle_event and not is_masquerading_as_specific_student(user, course_id):
            handle_event(block, event)
        else:
            context = contexts.course_context_from_course_id(course_id)
            if block.runtime.user_id:
                context['user_id'] = block.runtime.user_id
            context['asides'] = {}
            for aside in block.runtime.get_asides(block):
                if hasattr(aside, 'get_event_context'):
                    aside_event_info = aside.get_event_context(event_type, event)
                    if aside_event_info is not None:
                        context['asides'][aside.scope_ids.block_type] = aside_event_info
            with tracker.get_tracker().context(event_type, context):
                track_function(event_type, event)

    def handle_completion_event(block, event):
        """
        Submit a completion object for the block.
        """
        if not completion_waffle.waffle().is_enabled(completion_waffle.ENABLE_COMPLETION_TRACKING):
            raise Http404
        else:
            BlockCompletion.objects.submit_completion(
                user=user,
                course_key=course_id,
                block_key=block.scope_ids.usage_id,
                completion=event['completion'],
            )

    def handle_grade_event(block, event):
        """
        Submit a grade for the block.
        """
        SCORE_PUBLISHED.send(
            sender=None,
            block=block,
            user=user,
            raw_earned=event['value'],
            raw_possible=event['max_value'],
            only_if_higher=event.get('only_if_higher'),
            score_deleted=event.get('score_deleted'),
        )

    def handle_deprecated_progress_event(block, event):
        """
        DEPRECATED: Submit a completion for the block represented by the
        progress event.

        This exists to support the legacy progress extension used by
        edx-solutions.  New XBlocks should not emit these events, but instead
        emit completion events directly.
        """
        if not completion_waffle.waffle().is_enabled(completion_waffle.ENABLE_COMPLETION_TRACKING):
            raise Http404
        else:
            requested_user_id = event.get('user_id', user.id)
            if requested_user_id != user.id:
                log.warning("{} tried to submit a completion on behalf of {}".format(user, requested_user_id))
                return
            BlockCompletion.objects.submit_completion(
                user=user,
                course_key=course_id,
                block_key=block.scope_ids.usage_id,
                completion=1.0,
            )

    def rebind_noauth_module_to_user(module, real_user):
        """
        A function that allows a module to get re-bound to a real user if it was previously bound to an AnonymousUser.

        Will only work within a module bound to an AnonymousUser, e.g. one that's instantiated by the noauth_handler.

        Arguments:
            module (any xblock type):  the module to rebind
            real_user (django.contrib.auth.models.User):  the user to bind to

        Returns:
            nothing (but the side effect is that module is re-bound to real_user)
        """
        if user.is_authenticated():
            err_msg = ("rebind_noauth_module_to_user can only be called from a module bound to "
                       "an anonymous user")
            log.error(err_msg)
            raise LmsModuleRenderError(err_msg)

        field_data_cache_real_user = FieldDataCache.cache_for_descriptor_descendents(
            course_id,
            real_user,
            module.descriptor,
            asides=XBlockAsidesConfig.possible_asides(),
        )
        student_data_real_user = KvsFieldData(DjangoKeyValueStore(field_data_cache_real_user))

        (inner_system, inner_student_data) = get_module_system_for_user(
            user=real_user,
            student_data=student_data_real_user,  # These have implicit user bindings, rest of args considered not to
            descriptor=module.descriptor,
            course_id=course_id,
            track_function=track_function,
            xqueue_callback_url_prefix=xqueue_callback_url_prefix,
            position=position,
            wrap_xmodule_display=wrap_xmodule_display,
            grade_bucket_type=grade_bucket_type,
            static_asset_path=static_asset_path,
            user_location=user_location,
            request_token=request_token,
            course=course
        )

        module.descriptor.bind_for_student(
            inner_system,
            real_user.id,
            [
                partial(OverrideFieldData.wrap, real_user, course),
                partial(LmsFieldData, student_data=inner_student_data),
            ],
        )

        module.descriptor.scope_ids = (
            module.descriptor.scope_ids._replace(user_id=real_user.id)
        )
        module.scope_ids = module.descriptor.scope_ids  # this is needed b/c NamedTuples are immutable
        # now bind the module to the new ModuleSystem instance and vice-versa
        module.runtime = inner_system
        inner_system.xmodule_instance = module

    # Build a list of wrapping functions that will be applied in order
    # to the Fragment content coming out of the xblocks that are about to be rendered.
    block_wrappers = []

    if is_masquerading_as_specific_student(user, course_id):
        block_wrappers.append(filter_displayed_blocks)

    if settings.FEATURES.get("LICENSING", False):
        block_wrappers.append(wrap_with_license)

    # Wrap the output display in a single div to allow for the XModule
    # javascript to be bound correctly
    if wrap_xmodule_display is True:
        block_wrappers.append(partial(
            wrap_xblock,
            'LmsRuntime',
            extra_data={'course-id': course_id.to_deprecated_string()},
            usage_id_serializer=lambda usage_id: quote_slashes(usage_id.to_deprecated_string()),
            request_token=request_token,
        ))

    # TODO (cpennington): When modules are shared between courses, the static
    # prefix is going to have to be specific to the module, not the directory
    # that the xml was loaded from

    # Rewrite urls beginning in /static to point to course-specific content
    block_wrappers.append(partial(
        replace_static_urls,
        getattr(descriptor, 'data_dir', None),
        course_id=course_id,
        static_asset_path=static_asset_path or descriptor.static_asset_path
    ))

    # Allow URLs of the form '/course/' refer to the root of multicourse directory
    #   hierarchy of this course
    block_wrappers.append(partial(replace_course_urls, course_id))

    # this will rewrite intra-courseware links (/jump_to_id/<id>). This format
    # is an improvement over the /course/... format for studio authored courses,
    # because it is agnostic to course-hierarchy.
    # NOTE: module_id is empty string here. The 'module_id' will get assigned in the replacement
    # function, we just need to specify something to get the reverse() to work.
    block_wrappers.append(partial(
        replace_jump_to_id_urls,
        course_id,
        reverse('jump_to_id', kwargs={'course_id': course_id.to_deprecated_string(), 'module_id': ''}),
    ))

    if settings.FEATURES.get('DISPLAY_DEBUG_INFO_TO_STAFF'):
        if is_masquerading_as_specific_student(user, course_id):
            # When masquerading as a specific student, we want to show the debug button
            # unconditionally to enable resetting the state of the student we are masquerading as.
            # We already know the user has staff access when masquerading is active.
            staff_access = True
            # To figure out whether the user has instructor access, we temporarily remove the
            # masquerade_settings from the real_user.  With the masquerading settings in place,
            # the result would always be "False".
            masquerade_settings = user.real_user.masquerade_settings
            del user.real_user.masquerade_settings
            instructor_access = bool(has_access(user.real_user, 'instructor', descriptor, course_id))
            user.real_user.masquerade_settings = masquerade_settings
        else:
            staff_access = has_access(user, 'staff', descriptor, course_id)
            instructor_access = bool(has_access(user, 'instructor', descriptor, course_id))
        if staff_access:
            block_wrappers.append(partial(add_staff_markup, user, instructor_access, disable_staff_debug_info))

    # These modules store data using the anonymous_student_id as a key.
    # To prevent loss of data, we will continue to provide old modules with
    # the per-student anonymized id (as we have in the past),
    # while giving selected modules a per-course anonymized id.
    # As we have the time to manually test more modules, we can add to the list
    # of modules that get the per-course anonymized id.
    is_pure_xblock = isinstance(descriptor, XBlock) and not isinstance(descriptor, XModuleDescriptor)
    module_class = getattr(descriptor, 'module_class', None)
    is_lti_module = not is_pure_xblock and issubclass(module_class, LTIModule)
    if is_pure_xblock or is_lti_module:
        anonymous_student_id = anonymous_id_for_user(user, course_id)
    else:
        anonymous_student_id = anonymous_id_for_user(user, None)

    field_data = LmsFieldData(descriptor._field_data, student_data)  # pylint: disable=protected-access

    user_is_staff = bool(has_access(user, u'staff', descriptor.location, course_id))

    system = LmsModuleSystem(
        track_function=track_function,
        render_template=render_to_string,
        static_url=settings.STATIC_URL,
        xqueue=xqueue,
        # TODO (cpennington): Figure out how to share info between systems
        filestore=descriptor.runtime.resources_fs,
        get_module=inner_get_module,
        user=user,
        debug=settings.DEBUG,
        hostname=settings.SITE_NAME,
        # TODO (cpennington): This should be removed when all html from
        # a module is coming through get_html and is therefore covered
        # by the replace_static_urls code below
        replace_urls=partial(
            static_replace.replace_static_urls,
            data_directory=getattr(descriptor, 'data_dir', None),
            course_id=course_id,
            static_asset_path=static_asset_path or descriptor.static_asset_path,
        ),
        replace_course_urls=partial(
            static_replace.replace_course_urls,
            course_key=course_id
        ),
        replace_jump_to_id_urls=partial(
            static_replace.replace_jump_to_id_urls,
            course_id=course_id,
            jump_to_id_base_url=reverse('jump_to_id', kwargs={'course_id': course_id.to_deprecated_string(), 'module_id': ''})
        ),
        node_path=settings.NODE_PATH,
        publish=publish,
        anonymous_student_id=anonymous_student_id,
        course_id=course_id,
        cache=cache,
        can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)),
        get_python_lib_zip=(lambda: get_python_lib_zip(contentstore, course_id)),
        # TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington)
        mixins=descriptor.runtime.mixologist._mixins,  # pylint: disable=protected-access
        wrappers=block_wrappers,
        get_real_user=user_by_anonymous_id,
        services={
            'fs': FSService(),
            'field-data': field_data,
            'user': DjangoXBlockUserService(user, user_is_staff=user_is_staff),
            'verification': VerificationService(),
            'proctoring': ProctoringService(),
            'milestones': milestones_helpers.get_service(),
            'credit': CreditService(),
            'bookmarks': BookmarksService(user=user),
        },
        get_user_role=lambda: get_user_role(user, course_id),
        descriptor_runtime=descriptor._runtime,  # pylint: disable=protected-access
        rebind_noauth_module_to_user=rebind_noauth_module_to_user,
        user_location=user_location,
        request_token=request_token,
    )

    # pass position specified in URL to module through ModuleSystem
    if position is not None:
        try:
            position = int(position)
        except (ValueError, TypeError):
            log.exception('Non-integer %r passed as position.', position)
            position = None

    system.set('position', position)

    system.set(u'user_is_staff', user_is_staff)
    system.set(u'user_is_admin', bool(has_access(user, u'staff', 'global')))
    system.set(u'user_is_beta_tester', CourseBetaTesterRole(course_id).has_user(user))
    system.set(u'days_early_for_beta', descriptor.days_early_for_beta)

    # make an ErrorDescriptor -- assuming that the descriptor's system is ok
    if has_access(user, u'staff', descriptor.location, course_id):
        system.error_descriptor_class = ErrorDescriptor
    else:
        system.error_descriptor_class = NonStaffErrorDescriptor

    return system, field_data
def _section_recap(request, course, recap_blocks, access):
    """Provide data for the Recap dashboard section """

    course_key = course.id
    recap_items = []
    recap_fragments = []

    # Get list of enrolled students for this course

    user_list = User.objects.filter(
        courseenrollment__course_id=course_key,
        courseenrollment__is_active=1,
    ).order_by('username').select_related('profile')

    for block in recap_blocks:
        recap_items.append({
            'name':
            block.display_name,
            'block_list':
            block.xblock_list,
            'url_base':
            reverse(
                'xblock_view',
                args=[course.id, block.location, 'recap_blocks_listing_view']),
            'make_pdf_json':
            reverse('xblock_handler',
                    args=[course.id, block.location, 'make_pdf_json']),
        })

    recap_block = recap_blocks[0]
    block, __ = get_module_by_usage_id(request,
                                       unicode(course_key),
                                       unicode(recap_block.location),
                                       disable_staff_debug_info=True,
                                       course=course)
    # Set up recap instructor dashboard fragment, pass data to the context
    fragment = block.render('recap_blocks_listing_view',
                            context={
                                'recap_items': recap_items,
                                'users': user_list,
                            })
    # Wrap the fragment and get all resources associated with this XBlock view
    fragment = wrap_xblock(
        'LmsRuntime',
        recap_block,
        'recap_blocks_listing_view',
        fragment,
        None,
        extra_data={"course-id": unicode(course_key)},
        usage_id_serializer=lambda usage_id: quote_slashes(unicode(usage_id)),
        # Generate a new request_token here at random, because this module isn't connected to any other
        # xblock rendering.
        request_token=uuid.uuid1().get_hex())

    section_data = {
        'fragment': fragment,
        'users': user_list,
        'section_key': 'recap',
        'section_display_name': _('Recap'),
        'access': access,
        'course_id': unicode(course_key)
    }

    return section_data
예제 #37
0
 def test_inverse(self, test_string):
     self.assertEquals(test_string,
                       unquote_slashes(quote_slashes(test_string)))
 def test_inverse(self, test_string):
     assert test_string == unquote_slashes(quote_slashes(test_string))