use new XMLHttpRequest for file upload posting; cosmetic owner display fix
Tara L Andrews [Tue, 18 Sep 2012 20:18:37 +0000 (22:18 +0200)]
lib/stemmaweb/Controller/Root.pm
root/js/componentload.js

index abc1952..143adc9 100644 (file)
@@ -1,5 +1,4 @@
 package stemmaweb::Controller::Root;
-use File::Temp;
 use Moose;
 use namespace::autoclean;
 use Text::Tradition::Analysis qw/ run_analysis /;
@@ -90,8 +89,7 @@ sub directory :Local :Args(0) {
        { name: <name>,
          language: <language>,
          public: <is_public>,
-         filename: <filename>,
-         file: <filedata> }
+         file: <fileupload> }
  
 Creates a new tradition belonging to the logged-in user, with the given name
 and the collation given in the uploaded file. The file type is indicated via
@@ -100,7 +98,6 @@ name of the new tradition.
  
 =cut
 
-## TODO Figure out how to mimic old-style HTML file uploads in AJAX / HTML5
 sub newtradition :Local :Args(0) {
        my( $self, $c ) = @_;
        return _json_error( $c, 403, 'Cannot save a tradition without being logged in' )
@@ -109,18 +106,17 @@ sub newtradition :Local :Args(0) {
        my $user = $c->user->get_object;
        # Grab the file upload, check its name/extension, and call the
        # appropriate parser(s).
-       my $upload = File::Temp->new();
-       print $upload $c->request->param('file');
-       close $upload;
+       $DB::single = 1;
+       my $upload = $c->request->upload('file');
        my $name = $c->request->param('name') || 'Uploaded tradition';
        my $lang = $c->request->param( 'language' ) || 'Default';
        my $public = $c->request->param( 'public' ) ? 1 : undef;
-       my( $ext ) = $c->request->param( 'filename' ) =~ /\.(\w+)$/;
+       my( $ext ) = $upload->filename =~ /\.(\w+)$/;
        my %newopts = (
                'name' => $name,
                'language' => $lang,
                'public' => $public,
-               'file' => $upload->filename
+               'file' => $upload->tempname
                );
 
        my $tradition;
@@ -276,7 +272,7 @@ sub textinfo :Local :Args(1) {
                name => $tradition->name,
                language => $tradition->language,
                public => $tradition->public || 0,
-               owner => $tradition->user ? $tradition->user->id : undef,
+               owner => $tradition->user ? $tradition->user->email : undef,
                witnesses => [ map { $_->sigil } $tradition->witnesses ],
        };
        my @stemmasvg = map { $_->as_svg() } $tradition->stemmata;
index 1cbc2ac..243d8f7 100644 (file)
@@ -193,19 +193,73 @@ function file_selected( e ) {
        }
 }
 
+// Implement our own AJAX method that uses the features of XMLHttpRequest2
+// but try to let it have a similar interface to jquery.post
+// The data var needs to be a FormData() object.
+// The callback will be given a single argument, which is the response data
+// of the given type.
+
+function post_xhr2( url, data, cb, type ) {
+       if( !type ) {
+               type = 'json';
+       }
+       var xhr = new XMLHttpRequest();
+       // Set the expected response type
+       if( type === 'data' ) {
+               xhr.responseType = 'blob';
+       } else if( type === 'xml' ) {
+               xhr.responseType = 'document';
+       } 
+       // Post the form
+       // Gin up an AJAX settings object
+       $.ajaxSetup({ url: url, type: 'POST' });
+       xhr.open( 'POST', url, true );
+       // Handle the results
+       xhr.onload = function( e ) {
+               // Get the response and parse it
+               // Call the callback with the response, whatever it was
+               var xhrs = e.target;
+               if( xhrs.status > 199 && xhrs.status < 300  ) { // Success
+                       var resp;
+                       if( type === 'json' ) {
+                               resp = $.parseJSON( xhrs.responseText );
+                       } else if ( type === 'xml' ) {
+                               resp = xhrs.responseXML;
+                       } else if ( type === 'text' ) {
+                               resp = xhrs.responseText;
+                       } else {
+                               resp = xhrs.response;
+                       }
+                       cb( resp );
+               } else {
+                       // Trigger the ajaxError...
+                       _trigger_ajaxerror( e );
+               }
+       };
+       xhr.onerror = _trigger_ajaxerror;
+       xhr.onabort = _trigger_ajaxerror;
+       xhr.send( data );
+}
+
+function _trigger_ajaxerror( e ) {
+       var xhr = e.target;
+       var thrown = xhr.statusText || 'Request error';
+       jQuery.event.trigger( 'ajaxError', [ xhr, $.ajaxSettings, thrown ]);
+}
+
 function upload_new () {
        // Serialize the upload form, get the file and attach it to the request,
        // POST the lot and handle the response.
        var newfile = $('#new_file').get(0).files[0];
        var reader = new FileReader();
        reader.onload = function( evt ) {
-               var formvals = $('#new_tradition').serializeArray();
-               var params = { 'file': evt.target.result, 'filename': newfile.name };
-               $.each( formvals, function( i, o ) {
-                       params[o.name] = o.value;
-               });
+               var data = new FormData();
+               $.each( $('#new_tradition').serializeArray(), function( i, o ) {
+                               data.append( o.name, o.value );
+                       });
+               data.append( 'file', newfile );
                var upload_url = _get_url([ 'newtradition' ]);
-               $.post( upload_url, params, function( ret ) {
+               post_xhr2( upload_url, data, function( ret ) {
                        if( ret.id ) {
                                $('#upload-collation-dialog').dialog('close');
                                refreshDirectory();
@@ -214,7 +268,7 @@ function upload_new () {
                                $('#upload_status').empty().append( 
                                        $('<span>').attr('class', 'error').append( ret.error ) );
                        }
-               });
+               }, 'json' );
        };
        reader.onerror = function( evt ) {
                var err_resp = 'File read error';
@@ -392,6 +446,7 @@ $(document).ready( function() {
                        // Set the upload button to its correct state based on
                        // whether a file is loaded
                        file_selected( $('#new_file').get(0) );
+                       $('#upload_status').empty();
                }
        }).ajaxError( function(event, jqXHR, ajaxSettings, thrownError) {
                // Reset button state