? ctools_modal_file_uploads.patch
Index: includes/ajax.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ctools/includes/ajax.inc,v
retrieving revision 1.6
diff -u -r1.6 ajax.inc
--- includes/ajax.inc	20 Apr 2009 21:29:46 -0000	1.6
+++ includes/ajax.inc	3 May 2009 01:41:35 -0000
@@ -315,7 +315,15 @@
  * to the AJAX requester.
  */
 function ctools_ajax_render($commands = array()) {
-  drupal_json($commands);
+  if ($_GET['ctools_multipart'] == 1) {
+    // We don't use drupal_json here because the header is not true. We're not really
+    // returning JSON, strictly-speaking, but rather JSON content wrapped in a <textarea>
+    // as per the "file uploads" example here: http://malsup.com/jquery/form/#code-samples
+    echo '<textarea>'. drupal_to_js($commands) .'</textarea>';
+  } 
+  else {
+    drupal_json($commands);
+  }
   exit;
 }
 
Index: js/modal.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ctools/js/modal.js,v
retrieving revision 1.9
diff -u -r1.9 modal.js
--- js/modal.js	24 Apr 2009 00:08:51 -0000	1.9
+++ js/modal.js	3 May 2009 01:41:36 -0000
@@ -136,20 +136,34 @@
   var object = $(this);
   try {
     url.replace('/nojs/', '/ajax/');
+    type = "POST";
+    error = function() { 
+          alert("An error occurred while attempting to process " + url); 
+        };
+    complete = function() {
+          object.removeClass('ctools-ajaxing');
+          $('.ctools-ajaxing', object).removeClass('ctools-ajaxing');
+        };
+    enctype = $(this).attr('enctype');
+    if (enctype == 'multipart/form-data') {
+      url = url+'?ctools_multipart=1';
+      success = Drupal.CTools.AJAX.iFrameJsonRespond;
+      iframe = true;
+    }
+    else {
+      success = Drupal.CTools.AJAX.respond;
+      iframe = false;
+    }
     $(this).ajaxSubmit({
-      type: "POST",
+      type: type,
       url: url,
       data: '',
       global: true,
-      success: Drupal.CTools.AJAX.respond,
-      error: function() { 
-        alert("An error occurred while attempting to process " + url); 
-      },
-      complete: function() {
-        object.removeClass('ctools-ajaxing');
-        $('.ctools-ajaxing', object).removeClass('ctools-ajaxing');
-      },
-      dataType: 'json'
+      success: success,
+      error: error,
+      complete: complete,
+      dataType: 'json',
+      iframe: iframe
     });
   }
   catch (err) {
@@ -162,6 +176,15 @@
 }
 
 /**
+ * Wrapper for handling JSON responses from an iframe submission
+ */
+Drupal.CTools.AJAX.iFrameJsonRespond = function(data) {
+  var myJson = eval(data);
+  Drupal.CTools.AJAX.respond(myJson);
+}
+
+
+/**
  * Bind links that will open modals to the appropriate function.
  */
 Drupal.behaviors.CToolsModal = function(context) {
Index: plugins/content_types/custom/custom.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ctools/plugins/content_types/custom/custom.inc,v
retrieving revision 1.5
diff -u -r1.5 custom.inc
--- plugins/content_types/custom/custom.inc	22 Apr 2009 22:31:29 -0000	1.5
+++ plugins/content_types/custom/custom.inc	3 May 2009 01:41:36 -0000
@@ -96,6 +96,14 @@
   );
   $parents[] = 'format';
   $form['format'] = filter_form($conf['format'], 1, $parents);
+  
+  // This won't actually do anything, but demonstrates the functionality.
+  $form['generic_item_file'] = array(
+    '#title' => t('Example File Upload'),
+    '#type' => 'file',
+    '#size' => '50',
+  );
+  $form['#attributes']['enctype'] = 'multipart/form-data';
 
   return $form;
 }
@@ -107,4 +115,6 @@
   foreach (array_keys($form_state['plugin']['defaults']) as $key) {
     $form_state['conf'][$key] = $form_state['values'][$key];
   }
+  // Just to show that we did it, dump $_FILES.
+  drupal_set_message("<pre>FILE UPLOAD:\n\n". print_r($_FILES, 1) .'<pre>');
 }
