import logging import sys from functools import partial from django.http import HttpResponse, Http404, HttpResponseBadRequest, HttpResponseForbidden from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from mitxmako.shortcuts import render_to_response from xmodule_modifiers import replace_static_urls, wrap_xmodule, save_module # pylint: disable=F0401 from xmodule.error_module import ErrorDescriptor from xmodule.errortracker import exc_info_to_str from xmodule.exceptions import NotFoundError, ProcessingError from xmodule.modulestore import Location from xmodule.modulestore.django import modulestore from xmodule.modulestore.mongo import MongoUsage from xmodule.x_module import ModuleSystem from xblock.runtime import DbModel from util.sandboxing import can_execute_unsafe_code import static_replace from .session_kv_store import SessionKeyValueStore from .requests import render_from_lms from .access import has_access from ..utils import get_course_for_item __all__ = ['preview_dispatch', 'preview_component'] log = logging.getLogger(__name__) @login_required def preview_dispatch(request, preview_id, location, dispatch=None): """ Dispatch an AJAX action to a preview XModule Expects a POST request, and passes the arguments to the module preview_id (str): An identifier specifying which preview this module is used for location: The Location of the module to dispatch to dispatch: The action to execute """ descriptor = modulestore().get_item(location) instance = load_preview_module(request, preview_id, descriptor) # Let the module handle the AJAX try: ajax_return = instance.handle_ajax(dispatch, request.POST) # Save any module data that has changed to the underlying KeyValueStore instance.save() except NotFoundError: log.exception("Module indicating to user that request doesn't exist") raise Http404 except ProcessingError: log.warning("Module raised an error while processing AJAX request", exc_info=True) return HttpResponseBadRequest() except: log.exception("error processing ajax call") raise return HttpResponse(ajax_return) @login_required def preview_component(request, location): "Return the HTML preview of a component" # TODO (vshnayder): change name from id to location in coffee+html as well. if not has_access(request.user, location): return HttpResponseForbidden() component = modulestore().get_item(location) return render_to_response('component.html', { 'preview': get_module_previews(request, component)[0], 'editor': wrap_xmodule(component.get_html, component, 'xmodule_edit.html')(), }) def preview_module_system(request, preview_id, descriptor): """ Returns a ModuleSystem for the specified descriptor that is specialized for rendering module previews. request: The active django request preview_id (str): An identifier specifying which preview this module is used for descriptor: An XModuleDescriptor """ def preview_model_data(descriptor): "Helper method to create a DbModel from a descriptor" return DbModel( SessionKeyValueStore(request, descriptor._model_data), descriptor.module_class, preview_id, MongoUsage(preview_id, descriptor.location.url()), ) course_id = get_course_for_item(descriptor.location).location.course_id return ModuleSystem( ajax_url=reverse('preview_dispatch', args=[preview_id, descriptor.location.url(), '']).rstrip('/'), # TODO (cpennington): Do we want to track how instructors are using the preview problems? track_function=lambda event_type, event: None, filestore=descriptor.system.resources_fs, get_module=partial(load_preview_module, request, preview_id), render_template=render_from_lms, debug=True, replace_urls=partial(static_replace.replace_static_urls, data_directory=None, course_namespace=descriptor.location), user=request.user, xblock_model_data=preview_model_data, can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)), ) def load_preview_module(request, preview_id, descriptor): """ Return a preview XModule instantiated from the supplied descriptor. request: The active django request preview_id (str): An identifier specifying which preview this module is used for descriptor: An XModuleDescriptor """ system = preview_module_system(request, preview_id, descriptor) try: module = descriptor.xmodule(system) except: log.debug("Unable to load preview module", exc_info=True) module = ErrorDescriptor.from_descriptor( descriptor, error_msg=exc_info_to_str(sys.exc_info()) ).xmodule(system) # cdodge: Special case if module.location.category == 'static_tab': module.get_html = wrap_xmodule( module.get_html, module, "xmodule_tab_display.html", ) else: module.get_html = wrap_xmodule( module.get_html, module, "xmodule_display.html", ) module.get_html = replace_static_urls( module.get_html, getattr(module, 'data_dir', module.location.course), course_namespace=Location([module.location.tag, module.location.org, module.location.course, None, None]) ) module.get_html = save_module( module.get_html, module ) return module def get_module_previews(request, descriptor): """ Returns a list of preview XModule html contents. One preview is returned for each pair of states returned by get_sample_state() for the supplied descriptor. descriptor: An XModuleDescriptor """ preview_html = [] for idx, (_instance_state, _shared_state) in enumerate(descriptor.get_sample_state()): module = load_preview_module(request, str(idx), descriptor) preview_html.append(module.get_html()) return preview_html