From 75532987ce9a24ba4324953abeb75ac466203fb4 Mon Sep 17 00:00:00 2001
From: Ned Batchelder <ned@nedbatchelder.com>
Date: Thu, 21 Nov 2013 20:40:20 -0500
Subject: [PATCH] Properly convert files from Webob to pure files.

Webob represents uploaded files as cgi.FieldStorage objects.  The
XModule code expects pure Python file objects.  Each FieldStorage object
is wrapped to present the proper file interface, with file names.

LMS-1492
---
 .../xmodule/xmodule/tests/test_capa_module.py |  1 -
 .../xmodule/tests/test_xblock_wrappers.py     |  2 +-
 common/lib/xmodule/xmodule/x_module.py        | 27 ++++++++++++++++++-
 requirements/edx/github.txt                   |  2 +-
 4 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/common/lib/xmodule/xmodule/tests/test_capa_module.py b/common/lib/xmodule/xmodule/tests/test_capa_module.py
index 9db5022209a..b7b7d8b6f40 100644
--- a/common/lib/xmodule/xmodule/tests/test_capa_module.py
+++ b/common/lib/xmodule/xmodule/tests/test_capa_module.py
@@ -590,7 +590,6 @@ class CapaModuleTest(unittest.TestCase):
         for fpath, fileobj in kwargs['files'].iteritems():
             self.assertEqual(fpath, fileobj.name)
 
-    @unittest.expectedFailure
     def test_check_problem_with_files_as_xblock(self):
         # Check a problem with uploaded files, using the XBlock API.
         # pylint: disable=W0212
diff --git a/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py b/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
index 258ee5b0382..c8ac5fcdd35 100644
--- a/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
+++ b/common/lib/xmodule/xmodule/tests/test_xblock_wrappers.py
@@ -257,7 +257,7 @@ class TestXModuleHandler(TestXBlockWrapper):
     def setUp(self):
         self.module = XModule(descriptor=Mock(), field_data=Mock(), runtime=Mock(), scope_ids=Mock())
         self.module.handle_ajax = Mock(return_value='{}')
-        self.request = Mock()
+        self.request = webob.Request({})
 
     def test_xmodule_handler_passed_data(self):
         self.module.xmodule_handler(self.request)
diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py
index e644185c8c1..cd5a5ef3d90 100644
--- a/common/lib/xmodule/xmodule/x_module.py
+++ b/common/lib/xmodule/xmodule/x_module.py
@@ -8,6 +8,7 @@ from lxml import etree
 from collections import namedtuple
 from pkg_resources import resource_listdir, resource_string, resource_isdir
 from webob import Response
+from webob.multidict import MultiDict
 
 from xmodule.modulestore import Location
 from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError
@@ -406,7 +407,31 @@ class XModule(XModuleMixin, HTMLSnippet, XBlock):  # pylint: disable=abstract-me
         """
         XBlock handler that wraps `handle_ajax`
         """
-        response_data = self.handle_ajax(suffix, request.POST)
+        class FileObjForWebobFiles(object):
+            """
+            Turn Webob cgi.FieldStorage uploaded files into pure file objects.
+
+            Webob represents uploaded files as cgi.FieldStorage objects, which
+            have a .file attribute.  We wrap the FieldStorage object, delegating
+            attribute access to the .file attribute.  But the files have no
+            name, so we carry the FieldStorage .filename attribute as the .name.
+
+            """
+            def __init__(self, webob_file):
+                self.file = webob_file.file
+                self.name = webob_file.filename
+
+            def __getattr__(self, name):
+                return getattr(self.file, name)
+
+        # WebOb requests have multiple entries for uploaded files.  handle_ajax
+        # expects a single entry as a list.
+        request_post = MultiDict(request.POST)
+        for key in set(request.POST.iterkeys()):
+            if hasattr(request.POST[key], "file"):
+                request_post[key] = map(FileObjForWebobFiles, request.POST.getall(key))
+
+        response_data = self.handle_ajax(suffix, request_post)
         return Response(response_data, content_type='application/json')
 
     def get_children(self):
diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt
index cf2c5283232..26d12f2af93 100644
--- a/requirements/edx/github.txt
+++ b/requirements/edx/github.txt
@@ -15,7 +15,7 @@
 -e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
 
 # Our libraries:
--e git+https://github.com/edx/XBlock.git@2daa4e54#egg=XBlock
+-e git+https://github.com/edx/XBlock.git@d6d2fc91#egg=XBlock
 -e git+https://github.com/edx/codejail.git@0a1b468#egg=codejail
 -e git+https://github.com/edx/diff-cover.git@v0.2.6#egg=diff_cover
 -e git+https://github.com/edx/js-test-tool.git@v0.1.4#egg=js_test_tool
-- 
GitLab