コード例 #1
0
def _section_send_email(course_key, access, course):
    """ Provide data for the corresponding bulk email section """
    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": course_key.to_deprecated_string()},
        usage_id_serializer=lambda usage_id: quote_slashes(usage_id.to_deprecated_string())
    )
    email_editor = fragment.content
    section_data = {
        'section_key': 'send_email',
        'section_display_name': _('Email'),
        'access': access,
        'send_email': reverse('send_email', kwargs={'course_id': course_key.to_deprecated_string()}),
        'editor': email_editor,
        'list_instructor_tasks_url': reverse(
            'list_instructor_tasks', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
        'email_background_tasks_url': reverse(
            'list_background_email_tasks', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
        'email_content_history_url': reverse(
            'list_email_content', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
    }
    return section_data
コード例 #2
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.
            return 'input_i4x-{0}-{1}-problem-{2}_{3}'.format(TEST_COURSE_ORG.lower(),
                                                              TEST_COURSE_NUMBER.replace('.', '_'),
                                                              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': self.course.id.to_deprecated_string(),
            'usage_id': quote_slashes(InstructorTaskModuleTestCase.problem_location(problem_url_name).to_deprecated_string()),
            'handler': 'xmodule_handler',
            'suffix': 'problem_check',
        })

        # we assume we have two responses, so assign them the correct identifiers.
        resp = self.client.post(modx_url, {
            get_input_id('2_1'): responses[0],
            get_input_id('3_1'): responses[1],
        })
        return resp
コード例 #3
0
    def handle_callback_and_get_display_name_from_event(self, mock_tracker, problem_display_name=None):
        """
        Creates a fake module, invokes the callback and extracts the display name from the emitted problem_check event.
        """
        descriptor_kwargs = {
            'category': 'problem',
            'data': self.problem_xml
        }
        if problem_display_name:
            descriptor_kwargs['display_name'] = problem_display_name

        descriptor = ItemFactory.create(**descriptor_kwargs)

        render.handle_xblock_callback(
            self.request,
            self.course.id,
            quote_slashes(str(descriptor.location)),
            'xmodule_handler',
            'problem_check',
        )

        self.assertEquals(len(mock_tracker.send.mock_calls), 1)
        mock_call = mock_tracker.send.mock_calls[0]
        event = mock_call[1][0]

        self.assertEquals(event['event_type'], 'problem_check')
        return event['context']['module']['display_name']
コード例 #4
0
def _section_send_email(course_key, access, course):
    """ Provide data for the corresponding bulk email section """
    # 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": course_key.to_deprecated_string()},
        usage_id_serializer=lambda usage_id: quote_slashes(usage_id.to_deprecated_string()),
        # 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()
    )
    email_editor = fragment.content
    section_data = {
        'section_key': 'send_email',
        'section_display_name': _('Email'),
        'access': access,
        'send_email': reverse('send_email', kwargs={'course_id': course_key.to_deprecated_string()}),
        'editor': email_editor,
        'list_instructor_tasks_url': reverse(
            'list_instructor_tasks', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
        'email_background_tasks_url': reverse(
            'list_background_email_tasks', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
        'email_content_history_url': reverse(
            'list_email_content', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
    }
    return section_data
コード例 #5
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
         ]
     ))
コード例 #6
0
 def test_xmodule_dispatch(self):
     request = self.request_factory.post("dummy_url", data={"position": 1})
     request.user = self.mock_user
     response = render.handle_xblock_callback(
         request,
         self.course_key.to_deprecated_string(),
         quote_slashes(self.location.to_deprecated_string()),
         "xmodule_handler",
         "goto_position",
     )
     self.assertIsInstance(response, HttpResponse)
コード例 #7
0
 def test_bad_location(self):
     request = self.request_factory.post("dummy_url")
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             self.course_key.to_deprecated_string(),
             quote_slashes(self.course_key.make_usage_key("chapter", "bad_location").to_deprecated_string()),
             "xmodule_handler",
             "goto_position",
         )
コード例 #8
0
 def test_bad_course_id(self):
     request = self.request_factory.post("dummy_url")
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             "bad_course_id",
             quote_slashes(self.location.to_deprecated_string()),
             "xmodule_handler",
             "goto_position",
         )
コード例 #9
0
 def test_missing_handler(self):
     request = self.request_factory.post("dummy_url")
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             self.course_key.to_deprecated_string(),
             quote_slashes(self.location.to_deprecated_string()),
             "bad_handler",
             "bad_dispatch",
         )
コード例 #10
0
 def test_missing_handler(self):
     request = self.request_factory.post('dummy_url')
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             self.course_id,
             quote_slashes(str(self.location)),
             'bad_handler',
             'bad_dispatch',
         )
コード例 #11
0
 def test_bad_xmodule_dispatch(self):
     request = self.request_factory.post('dummy_url')
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             self.course_key.to_deprecated_string(),
             quote_slashes(self.location.to_deprecated_string()),
             'xmodule_handler',
             'bad_dispatch',
         )
コード例 #12
0
 def test_bad_location(self):
     request = self.request_factory.post('dummy_url')
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             self.course_id,
             quote_slashes(str(Location('i4x', 'edX', 'toy', 'chapter', 'bad_location'))),
             'xmodule_handler',
             'goto_position',
         )
コード例 #13
0
def wrap_xblock(runtime_class, block, view, frag, context, display_name_only=False, extra_data=None):  # pylint: disable=unused-argument
    """
    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 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)
    css_classes = ['xblock', 'xblock-' + view]

    if isinstance(block, (XModule, XModuleDescriptor)):
        if view == 'student_view':
            # The block is acting as an XModule
            css_classes.append('xmodule_display')
        elif view == 'mobi_student_view':
            # 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_' + class_name)
        data['type'] = block.js_module_name
        shim_xmodule_js(frag)

    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'] = quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8'))

    template_context = {
        'content': block.display_name if display_name_only else frag.content,
        'classes': css_classes,
        'display_name': block.display_name_with_default,
        'data_attributes': ' '.join(u'data-{}="{}"'.format(key, value) for key, value in data.items()),
    }

    return wrap_fragment(frag, render_to_string('xblock_wrapper.html', template_context))
コード例 #14
0
 def test_xmodule_dispatch(self):
     request = self.request_factory.post('dummy_url', data={'position': 1})
     request.user = self.mock_user
     response = render.handle_xblock_callback(
         request,
         self.course_id,
         quote_slashes(str(self.location)),
         'xmodule_handler',
         'goto_position',
     )
     self.assertIsInstance(response, HttpResponse)
コード例 #15
0
 def test_bad_course_id(self):
     request = self.request_factory.post('dummy_url')
     request.user = self.mock_user
     with self.assertRaises(Http404):
         render.handle_xblock_callback(
             request,
             'bad_course_id',
             quote_slashes(str(self.location)),
             'xmodule_handler',
             'goto_position',
         )
コード例 #16
0
ファイル: preview.py プロジェクト: 6thfdwp/edx-platform
def handler_prefix(block, handler='', suffix=''):
    """
    Return a url prefix for XBlock handler_url. The full handler_url
    should be '{prefix}/{handler}/{suffix}?{query}'.

    Trailing `/`s are removed from the returned url.
    """
    return reverse('preview_handler', kwargs={
        'usage_id': quote_slashes(str(block.scope_ids.usage_id)),
        'handler': handler,
        'suffix': suffix,
    }).rstrip('/?')
コード例 #17
0
 def test_anonymous_handle_xblock_callback(self):
     dispatch_url = reverse(
         'xblock_handler',
         args=[
             self.course_key.to_deprecated_string(),
             quote_slashes(self.course_key.make_usage_key('videosequence', 'Toy_Videos').to_deprecated_string()),
             'xmodule_handler',
             'goto_position'
         ]
     )
     response = self.client.post(dispatch_url, {'position': 2})
     self.assertEquals(403, response.status_code)
コード例 #18
0
 def test_anonymous_handle_xblock_callback(self):
     dispatch_url = reverse(
         'xblock_handler',
         args=[
             'edX/toy/2012_Fall',
             quote_slashes('i4x://edX/toy/videosequence/Toy_Videos'),
             'xmodule_handler',
             'goto_position'
         ]
     )
     response = self.client.post(dispatch_url, {'position': 2})
     self.assertEquals(403, response.status_code)
コード例 #19
0
 def test_anonymous_handle_xblock_callback(self):
     dispatch_url = reverse(
         "xblock_handler",
         args=[
             self.course_key.to_deprecated_string(),
             quote_slashes(self.course_key.make_usage_key("videosequence", "Toy_Videos").to_deprecated_string()),
             "xmodule_handler",
             "goto_position",
         ],
     )
     response = self.client.post(dispatch_url, {"position": 2})
     self.assertEquals(403, response.status_code)
     self.assertEquals("Unauthenticated", response.content)
コード例 #20
0
    def get_problem(self):
        pun = 'H1P1'
        problem_location = "i4x://edX/graded/problem/%s" % pun

        modx_url = reverse('xblock_handler',
                           kwargs={'course_id': self.graded_course.id,
                                   'usage_id': quote_slashes(problem_location),
                                   'handler': 'xmodule_handler',
                                   'suffix': 'problem_get'})

        resp = self.client.get(modx_url)

        print "modx_url ", modx_url
        return resp
コード例 #21
0
    def get_problem(self):
        pun = 'H1P1'
        problem_location = self.graded_course.id.make_usage_key("problem", pun)

        modx_url = reverse('xblock_handler',
                           kwargs={'course_id': self.graded_course.id.to_deprecated_string(),
                                   'usage_id': quote_slashes(problem_location.to_deprecated_string()),
                                   'handler': 'xmodule_handler',
                                   'suffix': 'problem_get'})

        resp = self.client.get(modx_url)

        print "modx_url ", modx_url
        return resp
コード例 #22
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,
                                'usage_id': quote_slashes(InstructorTaskModuleTestCase.problem_location(problem_url_name)),
                                'handler': 'xmodule_handler',
                                'suffix': 'problem_get', })
     resp = self.client.post(modx_url, {})
     return resp
コード例 #23
0
 def test_too_large_file(self):
     inputfile = self._mock_file(size=1 + settings.STUDENT_FILEUPLOAD_MAX_SIZE)
     request = self.request_factory.post("dummy_url", data={"file_id": inputfile})
     request.user = self.mock_user
     self.assertEquals(
         render.handle_xblock_callback(
             request,
             self.course_key.to_deprecated_string(),
             quote_slashes(self.location.to_deprecated_string()),
             "dummy_handler",
         ).content,
         json.dumps(
             {
                 "success": 'Submission aborted! Your file "%s" is too large (max size: %d MB)'
                 % (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))
             }
         ),
     )
コード例 #24
0
    def get_problem(self):
        pun = "H1P1"
        problem_location = "i4x://edX/graded/problem/%s" % pun

        modx_url = reverse(
            "xblock_handler",
            kwargs={
                "course_id": self.graded_course.id,
                "usage_id": quote_slashes(problem_location),
                "handler": "xmodule_handler",
                "suffix": "problem_get",
            },
        )

        resp = self.client.get(modx_url)

        print "modx_url ", modx_url
        return resp
コード例 #25
0
    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': self.course.id,
                'usage_id': quote_slashes(problem_location),
                'handler': 'xmodule_handler',
                'suffix': dispatch,
            }
        )
コード例 #26
0
 def test_too_many_files(self):
     request = self.request_factory.post(
         'dummy_url',
         data={'file_id': (self._mock_file(), ) * (settings.MAX_FILEUPLOADS_PER_INPUT + 1)}
     )
     request.user = self.mock_user
     self.assertEquals(
         render.handle_xblock_callback(
             request,
             'dummy/course/id',
             quote_slashes(str(self.location)),
             'dummy_handler'
         ).content,
         json.dumps({
             'success': 'Submission aborted! Maximum %d files may be submitted at once' %
                        settings.MAX_FILEUPLOADS_PER_INPUT
         })
     )
コード例 #27
0
def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
    """
    Handler URL function for Studio
    """

    if thirdparty:
        raise NotImplementedError("edX Studio doesn't support third-party xblock handler urls")

    url = reverse('component_handler', kwargs={
        'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')),
        'handler': handler_name,
        'suffix': suffix,
    }).rstrip('/')

    if query:
        url += '?' + query

    return url
コード例 #28
0
 def test_too_many_files(self):
     request = self.request_factory.post(
         "dummy_url", data={"file_id": (self._mock_file(),) * (settings.MAX_FILEUPLOADS_PER_INPUT + 1)}
     )
     request.user = self.mock_user
     self.assertEquals(
         render.handle_xblock_callback(
             request,
             self.course_key.to_deprecated_string(),
             quote_slashes(self.location.to_deprecated_string()),
             "dummy_handler",
         ).content,
         json.dumps(
             {
                 "success": "Submission aborted! Maximum %d files may be submitted at once"
                 % settings.MAX_FILEUPLOADS_PER_INPUT
             }
         ),
     )
コード例 #29
0
 def test_too_large_file(self):
     inputfile = self._mock_file(size=1 + settings.STUDENT_FILEUPLOAD_MAX_SIZE)
     request = self.request_factory.post(
         'dummy_url',
         data={'file_id': inputfile}
     )
     request.user = self.mock_user
     self.assertEquals(
         render.handle_xblock_callback(
             request,
             'dummy/course/id',
             quote_slashes(str(self.location)),
             'dummy_handler'
         ).content,
         json.dumps({
             'success': 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %
                        (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))
         })
     )
コード例 #30
0
def _section_send_email(course, access):
    """ Provide data for the corresponding bulk email section """
    course_key = course.id

    # 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": course_key.to_deprecated_string()},
        usage_id_serializer=lambda usage_id: quote_slashes(usage_id.to_deprecated_string()),
        # 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(),
    )
    email_editor = fragment.content
    section_data = {
        "section_key": "send_email",
        "section_display_name": _("Email"),
        "access": access,
        "send_email": reverse("send_email", kwargs={"course_id": course_key.to_deprecated_string()}),
        "editor": email_editor,
        "list_instructor_tasks_url": reverse(
            "list_instructor_tasks", kwargs={"course_id": course_key.to_deprecated_string()}
        ),
        "email_background_tasks_url": reverse(
            "list_background_email_tasks", kwargs={"course_id": course_key.to_deprecated_string()}
        ),
        "email_content_history_url": reverse(
            "list_email_content", kwargs={"course_id": course_key.to_deprecated_string()}
        ),
    }
    return section_data
コード例 #31
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.
            return 'input_i4x-{0}-{1}-problem-{2}_{3}'.format(
                TEST_COURSE_ORG.lower(), TEST_COURSE_NUMBER.replace('.', '_'),
                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':
                self.course.id,
                'usage_id':
                quote_slashes(
                    InstructorTaskModuleTestCase.problem_location(
                        problem_url_name)),
                'handler':
                'xmodule_handler',
                'suffix':
                'problem_check',
            })

        # we assume we have two responses, so assign them the correct identifiers.
        resp = self.client.post(modx_url, {
            get_input_id('2_1'): responses[0],
            get_input_id('3_1'): responses[1],
        })
        return resp
コード例 #32
0
def _section_send_email(course_key, access, course):
    """ Provide data for the corresponding bulk email section """
    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": course_key.to_deprecated_string()},
        usage_id_serializer=lambda usage_id: quote_slashes(
            usage_id.to_deprecated_string()))
    email_editor = fragment.content
    section_data = {
        'section_key':
        'send_email',
        'section_display_name':
        _('Email'),
        'access':
        access,
        'send_email':
        reverse('send_email',
                kwargs={'course_id': course_key.to_deprecated_string()}),
        'editor':
        email_editor,
        'list_instructor_tasks_url':
        reverse('list_instructor_tasks',
                kwargs={'course_id': course_key.to_deprecated_string()}),
        'email_background_tasks_url':
        reverse('list_background_email_tasks',
                kwargs={'course_id': course_key.to_deprecated_string()}),
        'email_content_history_url':
        reverse('list_email_content',
                kwargs={'course_id': course_key.to_deprecated_string()}),
    }
    return section_data
コード例 #33
0
def _section_send_email(course, access):
    """ Provide data for the corresponding bulk email section """
    course_key = course.id

    # 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": course_key.to_deprecated_string()},
        usage_id_serializer=lambda usage_id: quote_slashes(usage_id.to_deprecated_string()),
        # 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()
    )
    email_editor = fragment.content
    section_data = {
        'section_key': 'send_email',
        'section_display_name': _('Email'),
        'access': access,
        'send_email': reverse('send_email', kwargs={'course_id': course_key.to_deprecated_string()}),
        'editor': email_editor,
        'list_instructor_tasks_url': reverse(
            'list_instructor_tasks', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
        'email_background_tasks_url': reverse(
            'list_background_email_tasks', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
        'email_content_history_url': reverse(
            'list_email_content', kwargs={'course_id': course_key.to_deprecated_string()}
        ),
    }
    return section_data
コード例 #34
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,
             'usage_id':
             quote_slashes(
                 InstructorTaskModuleTestCase.problem_location(
                     problem_url_name)),
             'handler':
             'xmodule_handler',
             'suffix':
             'problem_get',
         })
     resp = self.client.post(modx_url, {})
     return resp
コード例 #35
0
def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
    """
    Handler URL function for Studio
    """

    if thirdparty:
        raise NotImplementedError(
            "edX Studio doesn't support third-party xblock handler urls")

    url = reverse('component_handler',
                  kwargs={
                      'usage_id':
                      quote_slashes(
                          unicode(block.scope_ids.usage_id).encode('utf-8')),
                      'handler':
                      handler_name,
                      'suffix':
                      suffix,
                  }).rstrip('/')

    if query:
        url += '?' + query

    return url
コード例 #36
0
 def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
     return reverse('preview_handler', kwargs={
         'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')),
         'handler': handler_name,
         'suffix': suffix,
     }) + '?' + query
コード例 #37
0
def wrap_xblock(runtime_class,
                block,
                view,
                frag,
                context,
                display_name_only=False,
                extra_data=None):  # pylint: disable=unused-argument
    """
    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 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)
    css_classes = ['xblock', 'xblock-' + view]

    if isinstance(block, (XModule, XModuleDescriptor)):
        if view == 'student_view':
            # 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_' + class_name)
        data['type'] = block.js_module_name
        shim_xmodule_js(frag)

    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'] = quote_slashes(
            unicode(block.scope_ids.usage_id).encode('utf-8'))

    template_context = {
        'content':
        block.display_name if display_name_only else frag.content,
        'classes':
        css_classes,
        'display_name':
        block.display_name_with_default,
        'data_attributes':
        ' '.join(u'data-{}="{}"'.format(key, value)
                 for key, value in data.items()),
    }

    return wrap_fragment(
        frag, render_to_string('xblock_wrapper.html', template_context))
コード例 #38
0
 def get_url(self, dispatch):
     """Return item url with dispatch."""
     return reverse(
         'xblock_handler',
         args=(self.course.id, quote_slashes(self.item_url), 'xmodule_handler', dispatch)
     )
コード例 #39
0
 def test_escaped(self, test_string):
     self.assertNotIn('/', quote_slashes(test_string))
コード例 #40
0
 def test_inverse(self, test_string):
     self.assertEquals(test_string,
                       unquote_slashes(quote_slashes(test_string)))
コード例 #41
0
def get_module_system_for_user(
        user,
        field_data_cache,
        # Arguments preceding this comment have user binding, those following don't
        descriptor,
        course_id,
        track_function,
        xqueue_callback_url_prefix,
        position=None,
        wrap_xmodule_display=True,
        grade_bucket_type=None,
        static_asset_path='',
        user_location=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 field_data_cache, 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()

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

    def make_xqueue_callback(dispatch='score_update'):
        # 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
    }

    # This is a hacky way to pass settings to the combined open ended xmodule
    # It needs an S3 interface to upload images to S3
    # It needs the open ended grading interface in order to get peer grading to be done
    # this first checks to see if the descriptor is the correct one, and only sends settings if it is

    # Get descriptor metadata fields indicating needs for various settings
    needs_open_ended_interface = getattr(descriptor,
                                         "needs_open_ended_interface", False)
    needs_s3_interface = getattr(descriptor, "needs_s3_interface", False)

    # Initialize interfaces to None
    open_ended_grading_interface = None
    s3_interface = None

    # Create interfaces if needed
    if needs_open_ended_interface:
        open_ended_grading_interface = settings.OPEN_ENDED_GRADING_INTERFACE
        open_ended_grading_interface[
            'mock_peer_grading'] = settings.MOCK_PEER_GRADING
        open_ended_grading_interface[
            'mock_staff_grading'] = settings.MOCK_STAFF_GRADING
    if needs_s3_interface:
        s3_interface = {
            'access_key':
            getattr(settings, 'AWS_ACCESS_KEY_ID', ''),
            'secret_access_key':
            getattr(settings, 'AWS_SECRET_ACCESS_KEY', ''),
            'storage_bucket_name':
            getattr(settings, 'AWS_STORAGE_BUCKET_NAME', 'openended')
        }

    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, descriptor, field_data_cache, course_id, track_function,
            make_xqueue_callback, position, wrap_xmodule_display,
            grade_bucket_type, static_asset_path, user_location)

    def handle_grade_event(block, event_type, event):
        user_id = event.get('user_id', user.id)

        # Construct the key for the module
        key = KeyValueStore.Key(scope=Scope.user_state,
                                user_id=user_id,
                                block_scope_id=descriptor.location,
                                field_name='grade')

        student_module = field_data_cache.find_or_create(key)
        # Update the grades
        student_module.grade = event.get('value')
        student_module.max_grade = event.get('max_value')
        # Save all changes to the underlying KeyValueStore
        student_module.save()

        # Bin score into range and increment stats
        score_bucket = get_score_bucket(student_module.grade,
                                        student_module.max_grade)

        tags = [
            u"org:{}".format(course_id.org), u"course:{}".format(course_id),
            u"score_bucket:{0}".format(score_bucket)
        ]

        if grade_bucket_type is not None:
            tags.append('type:%s' % grade_bucket_type)

        dog_stats_api.increment("lms.courseware.question_answered", tags=tags)

    def publish(block, event_type, event):
        """A function that allows XModules to publish events."""
        if event_type == 'grade':
            handle_grade_event(block, event_type, event)
        else:
            track_function(event_type, event)

    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)

        (inner_system, inner_student_data) = get_module_system_for_user(
            real_user,
            field_data_cache_real_user,  # These have implicit user bindings, rest of args considered not to
            module.descriptor,
            course_id,
            track_function,
            xqueue_callback_url_prefix,
            position,
            wrap_xmodule_display,
            grade_bucket_type,
            static_asset_path,
            user_location)
        # rebinds module to a different student.  We'll change system, student_data, and scope_ids
        module.descriptor.bind_for_student(
            inner_system,
            LmsFieldData(module.descriptor._field_data, inner_student_data)  # pylint: disable=protected-access
        )
        module.descriptor.scope_ids = (
            module.descriptor.scope_ids._replace(user_id=real_user.id)  # pylint: disable=protected-access
        )
        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 = []

    # 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())))

    # 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 has_access(user, 'staff', descriptor, course_id):
            has_instructor_access = has_access(user, 'instructor', descriptor,
                                               course_id)
            block_wrappers.append(
                partial(add_staff_markup, user, has_instructor_access))

    # 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)

    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,
        open_ended_grading_interface=open_ended_grading_interface,
        s3_interface=s3_interface,
        cache=cache,
        can_execute_unsafe_code=(lambda: can_execute_unsafe_code(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={
            'i18n': ModuleI18nService(),
        },
        get_user_role=lambda: get_user_role(user, course_id),
        descriptor_runtime=descriptor.runtime,
        rebind_noauth_module_to_user=rebind_noauth_module_to_user,
        user_location=user_location,
    )

    # 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)

    if settings.FEATURES.get('ENABLE_PSYCHOMETRICS'):
        system.set(
            'psychometrics_handler',  # set callback for updating PsychometricsData
            make_psychometrics_data_update_handler(course_id, user,
                                                   descriptor.location))

    system.set(u'user_is_staff',
               has_access(user, u'staff', descriptor.location, course_id))
    system.set(u'user_is_admin', has_access(user, u'staff', 'global'))

    # 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, student_data