From 796a66de5f10b219c5ed196a8c97ed4bd3a227a2 Mon Sep 17 00:00:00 2001 From: Christopher Lenz Date: Sat, 30 Aug 2008 21:09:31 +0000 Subject: Implement attachment uploading in Futon. git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@690590 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 5 + LICENSE | 23 ++ share/www/browse/_upload_attachment.html | 36 ++ share/www/browse/document.html | 3 + share/www/script/browse.js | 30 ++ share/www/script/jquery.dialog.js | 3 + share/www/script/jquery.form.js | 601 +++++++++++++++++++++++++++++++ share/www/style/layout.css | 7 +- src/couchdb/couch_httpd.erl | 26 +- 9 files changed, 732 insertions(+), 2 deletions(-) create mode 100644 share/www/browse/_upload_attachment.html create mode 100644 share/www/script/jquery.form.js diff --git a/CHANGES b/CHANGES index 0a688565..a50d1678 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,11 @@ Packaging and System Integration: * Updated `configure.ac` to extended default library paths. * Removed inets configuration files. +Futon Utility Client: + + * Added pagination to the database listing page. + * Implemented attachment uploading from the document page. + Version 0.8.1-incubating ------------------------ diff --git a/LICENSE b/LICENSE index 53184176..2b72b4ab 100644 --- a/LICENSE +++ b/LICENSE @@ -239,6 +239,29 @@ For the share/www/script/jquery.js component: OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +For the share/www/script/jquery.form.js component: + + http://malsup.com/jquery/form/ + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + For the share/www/script/json2.js component: Public Domain diff --git a/share/www/browse/_upload_attachment.html b/share/www/browse/_upload_attachment.html new file mode 100644 index 00000000..941fcc47 --- /dev/null +++ b/share/www/browse/_upload_attachment.html @@ -0,0 +1,36 @@ + +
+

Upload Attachment

+
+

+ Please select the file you want to upload as an attachment to this + document. Please note that this will result in the immediate creation of + a new revision of the document, so it's not necessary to save the + document after the upload. +

+ + + + + +
 
+
+
+ + + +
+
diff --git a/share/www/browse/document.html b/share/www/browse/document.html index 98d8ec48..09136533 100644 --- a/share/www/browse/document.html +++ b/share/www/browse/document.html @@ -23,6 +23,7 @@ specific language governing permissions and limitations under the License. + @@ -61,6 +62,7 @@ specific language governing permissions and limitations under the License. $("#toolbar button.save").click(page.saveDocument); $("#toolbar button.add").click(page.addField); + $("#toolbar button.load").click(page.uploadAttachment); $("#toolbar button.delete").click(page.deleteDocument); }); @@ -76,6 +78,7 @@ specific language governing permissions and limitations under the License. diff --git a/share/www/script/browse.js b/share/www/script/browse.js index e64fa777..605e9d49 100644 --- a/share/www/script/browse.js +++ b/share/www/script/browse.js @@ -648,6 +648,36 @@ function CouchDocumentPage() { }); } + this.uploadAttachment = function() { + if (page.isDirty) { + alert("You need to save or revert any changes you have made to the " + + "document before you can attach a new file."); + return false; + } + $.showDialog("_upload_attachment.html", { + load: function(elem) { + $("input[name='_rev']", elem).val(page.doc._rev); + }, + submit: function(data, callback) { + if (!data._attachments || data._attachments.length == 0) { + callback({_attachments: "Please select a file to upload."}); + return; + } + var form = $("#upload-form"); + form.find("#progress").css("visibility", "visible"); + form.ajaxSubmit({ + url: db.uri + encodeURIComponent(page.docId), + success: function(resp) { + form.find("#progress").css("visibility", "hidden"); + page.isDirty = false; + location.href = "?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(docId); + } + }); + } + }); + } + window.onbeforeunload = function() { if (page.isDirty) { return "You've made changes to this document that have not been " + diff --git a/share/www/script/jquery.dialog.js b/share/www/script/jquery.dialog.js index 1fd3d572..199a1c51 100644 --- a/share/www/script/jquery.dialog.js +++ b/share/www/script/jquery.dialog.js @@ -75,6 +75,9 @@ $.each($("form :input", dialog).serializeArray(), function(i, field) { data[field.name] = field.value; }); + $("form :file", dialog).each(function() { + data[this.name] = this.value; // file inputs need special handling + }); options.submit(data, function callback(errors) { if (errors == null || errors == {}) { dismiss(); diff --git a/share/www/script/jquery.form.js b/share/www/script/jquery.form.js new file mode 100644 index 00000000..659baa98 --- /dev/null +++ b/share/www/script/jquery.form.js @@ -0,0 +1,601 @@ +/* + * jQuery Form Plugin + * version: 2.12 (06/07/2008) + * @requires jQuery v1.2.2 or later + * + * Examples and documentation at: http://malsup.com/jquery/form/ + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id$ + */ +(function($) { + +/* + Usage Note: + ----------- + Do not use both ajaxSubmit and ajaxForm on the same form. These + functions are intended to be exclusive. Use ajaxSubmit if you want + to bind your own submit handler to the form. For example, + + $(document).ready(function() { + $('#myForm').bind('submit', function() { + $(this).ajaxSubmit({ + target: '#output' + }); + return false; // <-- important! + }); + }); + + Use ajaxForm when you want the plugin to manage all the event binding + for you. For example, + + $(document).ready(function() { + $('#myForm').ajaxForm({ + target: '#output' + }); + }); + + When using ajaxForm, the ajaxSubmit function will be invoked for you + at the appropriate time. +*/ + +/** + * ajaxSubmit() provides a mechanism for immediately submitting + * an HTML form using AJAX. + */ +$.fn.ajaxSubmit = function(options) { + // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) + if (!this.length) { + log('ajaxSubmit: skipping submit process - no element selected'); + return this; + } + + if (typeof options == 'function') + options = { success: options }; + + options = $.extend({ + url: this.attr('action') || window.location.toString(), + type: this.attr('method') || 'GET' + }, options || {}); + + // hook for manipulating the form data before it is extracted; + // convenient for use with rich editors like tinyMCE or FCKEditor + var veto = {}; + this.trigger('form-pre-serialize', [this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); + return this; + } + + var a = this.formToArray(options.semantic); + if (options.data) { + options.extraData = options.data; + for (var n in options.data) + a.push( { name: n, value: options.data[n] } ); + } + + // give pre-submit callback an opportunity to abort the submit + if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSubmit callback'); + return this; + } + + // fire vetoable 'validate' event + this.trigger('form-submit-validate', [a, this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); + return this; + } + + var q = $.param(a); + + if (options.type.toUpperCase() == 'GET') { + options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; + options.data = null; // data is null for 'get' + } + else + options.data = q; // data is the query string for 'post' + + var $form = this, callbacks = []; + if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); + if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); + + // perform a load on the target only if dataType is not provided + if (!options.dataType && options.target) { + var oldSuccess = options.success || function(){}; + callbacks.push(function(data) { + $(options.target).html(data).each(oldSuccess, arguments); + }); + } + else if (options.success) + callbacks.push(options.success); + + options.success = function(data, status) { + for (var i=0, max=callbacks.length; i < max; i++) + callbacks[i](data, status, $form); + }; + + // are there files to upload? + var files = $('input:file', this).fieldValue(); + var found = false; + for (var j=0; j < files.length; j++) + if (files[j]) + found = true; + + // options.iframe allows user to force iframe mode + if (options.iframe || found) { + // hack to fix Safari hang (thanks to Tim Molendijk for this) + // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d + if ($.browser.safari && options.closeKeepAlive) + $.get(options.closeKeepAlive, fileUpload); + else + fileUpload(); + } + else + $.ajax(options); + + // fire 'notify' event + this.trigger('form-submit-notify', [this, options]); + return this; + + + // private function for handling file uploads (hat tip to YAHOO!) + function fileUpload() { + var form = $form[0]; + + if ($(':input[@name=submit]', form).length) { + alert('Error: Form elements must not be named "submit".'); + return; + } + + var opts = $.extend({}, $.ajaxSettings, options); + + var id = 'jqFormIO' + (new Date().getTime()); + var $io = $('