changeset 194:389cd14f0888

integrate jquery.file-upload as /+index2 view, adapt quickinstall script
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Tue, 26 Apr 2011 00:21:06 +0200
parents 17693a043442
children d75075769daa
files MoinMoin/apps/frontend/views.py MoinMoin/config/default.py MoinMoin/static/js/jfu.js MoinMoin/templates/index2.html MoinMoin/templates/itemviews.html quickinstall wikiconfig.py
diffstat 7 files changed, 383 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/apps/frontend/views.py	Mon Apr 25 23:29:23 2011 +0200
+++ b/MoinMoin/apps/frontend/views.py	Tue Apr 26 00:21:06 2011 +0200
@@ -17,7 +17,7 @@
 import time
 from itertools import chain
 
-from flask import request, url_for, flash, Response, redirect, session, abort
+from flask import request, url_for, flash, Response, redirect, session, abort, jsonify
 from flask import current_app as app
 from flask import g as flaskg
 from flaskext.themes import get_themes_list
@@ -77,6 +77,8 @@
 Disallow: /+rename/
 Disallow: /+revert/
 Disallow: /+index/
+Disallow: /+index2/
+Disallow: /+jfu-server/
 Disallow: /+sitemap/
 Disallow: /+similar_names/
 Disallow: /+quicklink/
@@ -479,6 +481,48 @@
         return redirect(url_for('frontend.show_item', item_name=item_name))
 
 
+# XXX this has some functional redundancy with "index", solve that later
+@frontend.route('/+index2/<itemname:item_name>', methods=['GET'])
+def index2(item_name):
+    # flat index using jquery.file-upload (see also jfu_server)
+    return render_template('index2.html',
+                           item_name=item_name,
+                          )
+
+@frontend.route('/+jfu-server/<itemname:item_name>', methods=['GET', 'POST'])
+def jfu_server(item_name):
+    """jquery-file-upload server component
+    """
+    if request.method == 'GET':
+        try:
+            item = Item.create(item_name)
+        except AccessDeniedError:
+            abort(403)
+        files = []
+        for full_name, rel_name, mimetype in item.flat_index():
+            url = url_for('show_item', item_name=full_name)
+            url_download = url_for('get_item', item_name=full_name)
+            files.append(dict(name=rel_name, url=url, url_download=url_download, size=0))
+        return jsonify(files=files)
+    if request.method == 'POST':
+        data_file = request.files.get('data_file')
+        subitem_name = data_file.filename
+        item_name = item_name + u'/' + subitem_name
+        try:
+            item = Item.create(item_name)
+            revno, size = item.modify()
+            item_modified.send(app._get_current_object(),
+                               item_name=item_name)
+            return jsonify(name=subitem_name,
+                           size=size,
+                           url=url_for('show_item', item_name=item_name, rev=revno),
+                           url_download=url_for('get_item', item_name=item_name, rev=revno),
+                          )
+        except AccessDeniedError:
+            abort(403)
+
+
+
 @frontend.route('/+index/<itemname:item_name>')
 def index(item_name):
     try:
--- a/MoinMoin/config/default.py	Mon Apr 25 23:29:23 2011 +0200
+++ b/MoinMoin/config/default.py	Tue Apr 26 00:21:06 2011 +0200
@@ -359,6 +359,7 @@
         ('frontend.modify_item', L_('Modify'), L_('Edit or Upload'), True, ),
         ('special.supplementation', None, None, False, ),
         ('frontend.index', L_('Index'), L_('List sub-items'), False, ),
+        ('frontend.index2', L_('Index'), L_('List sub-items'), False, ),
         ('special.comments', L_('Comments'), L_('Switch showing comments on or off'), True, ),
         ('frontend.highlight_item', L_('Highlight'), L_('Show with Syntax-Highlighting'), True, ),
         ('frontend.show_item_meta', L_('Meta'), L_('Display Metadata'), True, ),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/static/js/jfu.js	Tue Apr 26 00:21:06 2011 +0200
@@ -0,0 +1,289 @@
+/*
+ * jQuery File Upload User Interface Extended Plugin 4.4.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan, https://blueimp.net
+ * Copyright 2011, Thomas Waldmann (adapted for MoinMoin)
+ *
+ * Licensed under the MIT license:
+ * http://creativecommons.org/licenses/MIT/
+ */
+
+/*jslint regexp: false */
+/*global jQuery */
+
+(function ($) {
+    'use strict';
+
+    var UploadHandler,
+        methods;
+
+    // Emulate jQuery UI button (without states) if not available:
+    if (typeof $().button !== 'function') {
+        $.fn.button = function (options) {
+            return this.each(function () {
+                if (options === 'destroy') {
+                    $(this).removeClass(
+                        'ui-button ui-widget ui-state-default ui-corner-all' +
+                            ' ui-button-icon-only ui-button-text-icon-primary'
+                    ).html($(this).text());
+                } else {
+                    $(this)
+                        .addClass('ui-button ui-widget ui-state-default ui-corner-all')
+                        .addClass(
+                            options.text === false ? 'ui-button-icon-only' :
+                                'ui-button-text-icon-primary'
+                        )
+                        .html($('<span class="ui-button-text"/>').text($(this).text()))
+                        .prepend(
+                            $('<span class="ui-button-icon-primary ui-icon"/>')
+                                .addClass(options.icons.primary)
+                        );
+                }
+            });
+        };
+    }
+        
+    UploadHandler = function (container, options) {
+        var uploadHandler = this;
+
+        this.url = container.find('form:first').attr('action');
+        this.dropZone = container.find('form:first');
+        this.uploadTable = container.find('.files:first');
+        this.downloadTable = this.uploadTable;
+        this.progressAllNode = container.find('.file_upload_overall_progress div:first');
+        this.uploadTemplate = this.uploadTable.find('.file_upload_template:first');
+        this.downloadTemplate = this.uploadTable.find('.file_download_template:first');
+        this.multiButtons = container.find('.file_upload_buttons:first');
+        
+        this.formatFileName = function (name) {
+            return name.replace(/^.*[\/\\]/, '');
+        };
+        
+        this.enableDragToDesktop = function () {
+            var link = $(this),
+                url = link.get(0).href,
+                name = decodeURIComponent(url.split('/').pop()).replace(/:/g, '-'),
+                type = 'application/octet-stream';
+            link.bind('dragstart', function (event) {
+                try {
+                    event.originalEvent.dataTransfer
+                        .setData('DownloadURL', [type, name, url].join(':'));
+                } catch (e) {}
+            });
+        };
+
+        this.buildMultiUploadRow = function (files, handler) {
+            var rows = $('<tbody style="display:none;"/>');
+            $.each(files, function (index, file) {
+                var row = handler.buildUploadRow(files, index, handler).show(),
+                    cells = row.find(
+                        '.file_upload_progress, .file_upload_start, .file_upload_cancel'
+                    );
+                if (index) {
+                    cells.remove();
+                } else {
+                    cells.attr('rowspan', files.length);
+                }
+                rows.append(row);
+            });
+            return rows;
+        };
+
+        this.buildUploadRow = function (files, index, handler) {
+            if (typeof index !== 'number') {
+                return handler.buildMultiUploadRow(files, handler);
+            }
+            var file = files[index],
+                fileName = handler.formatFileName(file.name),
+                uploadRow = handler.uploadTemplate
+                    .clone().removeAttr('id');
+            uploadRow.find('.file_name')
+                .text(fileName);
+            uploadRow.find('.file_upload_start button')
+                .button({icons: {primary: 'ui-icon-circle-arrow-e'}, text: false});
+            uploadRow.find('.file_upload_cancel button')
+                .button({icons: {primary: 'ui-icon-cancel'}, text: false});
+            return uploadRow;
+        };
+
+        this.buildMultiDownloadRow = function (files, handler) {
+            var rows = $('<tbody style="display:none;"/>');
+            $.each(files, function (index, file) {
+                rows.append(handler.buildDownloadRow(file, handler).show());
+            });
+            return rows;
+        };
+
+        this.buildDownloadRow = function (file, handler) {
+            if ($.isArray(file)) {
+                return handler.buildMultiDownloadRow(file, handler);
+            }
+            var fileName = handler.formatFileName(file.name),
+                fileUrl = file.url,
+                fileUrlDownload = file.url_download,
+                downloadRow = handler.downloadTemplate
+                    .clone().removeAttr('id');
+            downloadRow.attr('data-id', file.id || file.name);
+            downloadRow.find('.file_name a')
+                .text(fileName);
+            downloadRow.find('.file_name a')
+                .attr('href', fileUrl || null);
+            downloadRow.find('.file_download a')
+                .text('DL');
+            downloadRow.find('.file_download a')
+                .attr('href', fileUrlDownload || null)
+                .each(handler.enableDragToDesktop);
+            return downloadRow;
+        };
+        
+        this.beforeSend = function (event, files, index, xhr, handler, callBack) {
+            var fileSize = null;
+            if (typeof index === 'undefined') {
+                fileSize = 0;
+                $.each(files, function (index, file) {
+                    if (file.size > fileSize) {
+                        fileSize = file.size;
+                    }
+                });
+            } else {
+                fileSize = files[index].size;
+            }
+            if (fileSize === 0) {
+                setTimeout(function () {
+                    handler.onAbort(event, files, index, xhr, handler);
+                }, 10000);
+                return;
+            }
+            uploadHandler.multiButtons.find('.file_upload_start:first, .file_upload_cancel:first').fadeIn();
+            handler.uploadRow.find('.file_upload_start button').click(function (e) {
+                $(this).fadeOut(function () {
+                    if (!$('.files .file_upload_start button:visible:first').length) {
+                        uploadHandler.multiButtons.find('.file_upload_start:first').fadeOut();
+                    }
+                });
+                callBack();
+                e.preventDefault();
+            });
+        };
+
+        this.onCompleteAll = function (list) {
+            if (!uploadHandler.uploadTable.find('.file_upload_progress div:visible:first').length) {
+                uploadHandler.multiButtons.find('.file_upload_start:first, .file_upload_cancel:first').fadeOut();
+            }
+        };
+
+        this.initEventHandlers = function () {
+            container.find('.file_upload_cancel button').live('click', function () {
+                setTimeout(function () {
+                    if (!uploadHandler.uploadTable.find('.file_upload_progress div:visible:first').length) {
+                        uploadHandler.multiButtons.find('.file_upload_start:first, .file_upload_cancel:first').fadeOut();
+                    }
+                }, 500);
+            });
+        };
+
+        this.destroyEventHandlers = function () {
+        };
+        
+        this.multiButtonHandler = function (e) {
+            uploadHandler.uploadTable.find(e.data.selector + ' button:visible').click();
+            e.preventDefault();
+        };
+        
+        this.initMultiButtons = function () {
+            uploadHandler.multiButtons.find('.file_upload_start:first')
+                .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
+                .bind('click', {selector: '.file_upload_start'}, uploadHandler.multiButtonHandler);
+            uploadHandler.multiButtons.find('.file_upload_cancel:first')
+                .button({icons: {primary: 'ui-icon-cancel'}})
+                .bind('click', {selector: '.file_upload_cancel'}, uploadHandler.multiButtonHandler);
+        };
+        
+        this.destroyMultiButtons = function () {
+            uploadHandler.multiButtons.find(
+                '.file_upload_start:first, .file_upload_cancel:first'
+            ).unbind('click', uploadHandler.multiButtonHandler).button('destroy').show();
+        };
+
+        this.initExtended = function () {
+            uploadHandler.initEventHandlers();
+            uploadHandler.initMultiButtons();
+        };
+
+        this.destroyExtended = function () {
+            uploadHandler.destroyEventHandlers();
+            uploadHandler.destroyMultiButtons();
+        };
+
+        $.extend(this, options);
+    };
+
+    methods = {
+        init : function (options) {
+            return this.each(function () {
+                $(this).fileUploadUI(new UploadHandler($(this), options));
+            });
+        },
+        
+        option: function (option, value, namespace) {
+            if (!option || (typeof option === 'string' && typeof value === 'undefined')) {
+                return $(this).fileUpload('option', option, value, namespace);
+            }
+            return this.each(function () {
+                $(this).fileUploadUI('option', option, value, namespace);
+            });
+        },
+            
+        destroy : function (namespace) {
+            return this.each(function () {
+                $(this).fileUploadUI('destroy', namespace);
+            });
+        },
+        
+        upload: function (files, namespace) {
+            return this.each(function () {
+                $(this).fileUploadUI('upload', files, namespace);
+            });
+        }
+    };
+    
+    $.fn.fileUploadUIX = function (method) {
+        if (methods[method]) {
+            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+        } else if (typeof method === 'object' || !method) {
+            return methods.init.apply(this, arguments);
+        } else {
+            $.error('Method "' + method + '" does not exist on jQuery.fileUploadUIX');
+        }
+    };
+    
+}(jQuery));
+
+
+/*
+ * jQuery File Upload Plugin JS Example 4.4.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://creativecommons.org/licenses/MIT/
+ */
+
+/*global $ */
+
+$(function () {
+    // Initialize jQuery File Upload (Extended User Interface Version):
+    $('#file_upload').fileUploadUIX();
+
+    // Load existing files:
+    $.getJSON($('#file_upload').fileUploadUIX('option', 'url'), function (files) {
+        var fileUploadOptions = $('#file_upload').fileUploadUIX('option');
+        $.each(files, function (index, file) {
+            fileUploadOptions.buildDownloadRow(file, fileUploadOptions)
+                .appendTo(fileUploadOptions.downloadTable).fadeIn();
+        });
+    });
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/index2.html	Tue Apr 26 00:21:06 2011 +0200
@@ -0,0 +1,37 @@
+{% extends theme("show.html") %}
+
+{% block theme_stylesheets %}
+{{ super() }}
+<link rel="stylesheet" href="{{ url_for('serve.files', name='fileupload', filename='jquery.fileupload-ui.css') }}">
+{% endblock %}
+
+{% block content %}
+    <h1>{{ _("Index of subitems of '%(item_name)s'", item_name=item_name) }}</h1>
+<div id="file_upload">
+    <form action="{{ url_for('jfu_server', item_name=item_name) }}" method="POST" enctype="multipart/form-data">
+        <input type="file" name="data_file" multiple>
+        <button type="submit">Upload</button>
+        <div class="file_upload_label">Upload files</div>
+    </form>
+    <div class="file_upload_overall_progress"><div style="display:none;"></div></div>
+    <div class="file_upload_buttons">
+        <button class="file_upload_start" style="display:none;">Start All</button> 
+        <button class="file_upload_cancel" style="display:none;">Cancel All</button> 
+    </div>
+    <table class="files">
+        <tr class="file_upload_template" style="display:none;">
+            <td class="file_upload_start"><button>Start</button></td>
+            <td class="file_upload_cancel"><button>Cancel</button></td>
+            <td class="file_name"></td>
+            <td class="file_upload_progress"><div></div></td>
+        </tr>
+        <tr class="file_download_template" style="display:none;">
+            <td class="file_download" colspan="2"><a></a></td>
+            <td class="file_name" colspan="2"><a></a></td>
+        </tr>
+    </table>
+</div>
+<script src="{{ url_for('serve.files', name='fileupload', filename='jquery.fileupload.js') }}"></script>
+<script src="{{ url_for('serve.files', name='fileupload', filename='jquery.fileupload-ui.js') }}"></script>
+<script src="{{ url_for('.static', filename='js/jfu.js') }}"></script>
+{% endblock %}
--- a/MoinMoin/templates/itemviews.html	Mon Apr 25 23:29:23 2011 +0200
+++ b/MoinMoin/templates/itemviews.html	Tue Apr 26 00:21:06 2011 +0200
@@ -2,7 +2,7 @@
 <ul class="moin-itemviews">
     {% for endpoint, label, title, check_exists in cfg.item_views if not endpoint in cfg.endpoints_excluded %}
         {% if (not check_exists or check_exists and exists) and endpoint in [
-               'frontend.show_item', 'frontend.index',
+               'frontend.show_item', 'frontend.index', 'frontend.index2',
                'frontend.highlight_item', 'frontend.show_item_meta', 'frontend.get_item',
                'frontend.get_item', 'frontend.history', 'frontend.backrefs', 'frontend.sitemap',
                'frontend.similar_names',
--- a/quickinstall	Mon Apr 25 23:29:23 2011 +0200
+++ b/quickinstall	Tue Apr 26 00:21:06 2011 +0200
@@ -12,8 +12,9 @@
         echo /usr/bin/curl -L -o $1 $2
         /usr/bin/curl -L -o $1 $2
     else
-        echo wget -nc -O $1 $2
-        wget -nc -O $1 $2
+        # github redirects to a url where its cert does not match :(
+        echo wget -nc --no-check-certificate -O $1 $2
+        wget -nc --no-check-certificate -O $1 $2
     fi
 }
 
@@ -30,6 +31,12 @@
 download env/svgedit.tgz http://static.moinmo.in/files/packages/svg-edit.tar.gz
 tar xz -C env/ -f env/svgedit.tgz
 
+mkdir env/jquery.fu
+download env/jquery.fu/jquery.fileupload.js https://www.github.com/blueimp/jQuery-File-Upload/raw/master/jquery.fileupload.js
+download env/jquery.fu/jquery.fileupload-ui.js https://www.github.com/blueimp/jQuery-File-Upload/raw/master/jquery.fileupload-ui.js
+download env/jquery.fu/jquery.fileupload-ui.css https://www.github.com/blueimp/jQuery-File-Upload/raw/master/jquery.fileupload-ui.css
+download env/jquery.fu/pbar-ani.gif https://www.github.com/blueimp/jQuery-File-Upload/raw/master/pbar-ani.gif
+
 mkdir env/jquery
 download env/jquery/jquery.min.js http://code.jquery.com/jquery-1.4.4.min.js
 
--- a/wikiconfig.py	Mon Apr 25 23:29:23 2011 +0200
+++ b/wikiconfig.py	Tue Apr 26 00:21:06 2011 +0200
@@ -61,6 +61,7 @@
         twikidraw = os.path.join(wikiconfig_dir, 'env', 'TWikiDrawPlugin'),
         svgedit = os.path.join(wikiconfig_dir, 'env', 'svg-edit'),
         docs = os.path.join(wikiconfig_dir, 'docs', '_build', 'html'),
+        fileupload = os.path.join(wikiconfig_dir, 'env', 'jquery.fu'),
     )
 
     # ^^^ DON'T TOUCH THIS EXCEPT IF YOU KNOW WHAT YOU DO ^^^