Commit 1d593744cace6fa44757c77b06803b5738707889

Add uploads support

  Add basic uploads support. This should improve both in terms of UI and in
terms of robustness, and functionality.
  
3434
3535PLUGIN_UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__))
3636 + '/static/user_plugins')
37PLUGIN_ALLOWED_EXTENSIONS = set(['js', 'css'])
3837
38ALLOWED_EXTENSIONS = set(['js', 'css', 'jpg', 'JPG', 'png', 'gif', 'PNG',
39 'svg', 'pdf'])
40#ALLOWED_EXTENSIONS = set(['js', 'css'])
41
3942FILE_UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)) +
4043 '/static/uploads')
4144
9595
9696def allowed_file(filename):
9797 return '.' in filename and \
98 filename.rsplit('.', 1)[1] in PLUGIN_ALLOWED_EXTENSIONS
98 filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
9999
100100
101101@app.errorhandler(404)
302302 #return flask.redirect(flask.url_for('uploaded_file',
303303 # filename=filename))
304304 return flask.jsonify(uploaded = True,
305 path=flask.url_for('static', filename =
305 path=flask.url_for('static', filename =\
306306 'user_plugins/'+ filename))
307307
308@app.route('/upload', methods=['GET', 'POST'])
309def upload():
310 if flask.request.method == 'POST':
311 print flask.request.files
312 file = flask.request.files['upload-file']
313 if file and allowed_file(file.filename):
314 print 'file ok'
315 filename = secure_filename(file.filename)
316 file.save(os.path.join(app.config['FILE_UPLOAD_FOLDER'], filename))
317
318 return flask.jsonify(uploaded = True, path =\
319 flask.url_for('static', filename =\
320 'uploads/' + filename))
321
322 else:
323 resp = flask.make_response()
324 print 'file not ok'
325 resp.status_code = 400
326 return resp
327
328 if flask.request.method == 'GET':
329 uploaded_files = os.listdir(app.config['FILE_UPLOAD_FOLDER'])
330 print uploaded_files
331 return flask.jsonify({'uploaded_files': uploaded_files})
332
333@app.route('/upload/<filename>', methods=['DELETE'])
334def removeFile(filename):
335 filepath = os.path.join(app.config['FILE_UPLOAD_FOLDER'], filename)
336 print filepath
337 res = os.remove(filepath)
338 print res
339 return '200 OK'
340
341
308342@app.route('/robots.txt')
309343@app.route('/crossdomain.xml')
310344def static_from_root():
347347
348348app.config.from_object(conf)
349349app.config['PLUGIN_UPLOAD_FOLDER'] = PLUGIN_UPLOAD_FOLDER
350app.config['FILE_UPLOAD_FOLDER'] = FILE_UPLOAD_FOLDER
350351
351352import logging,os
352353from logging import FileHandler
  
1111 'click .pagename .remove': 'removePage',
1212 'click #menu-config': 'showMenu',
1313 'click #footer-config': 'showFooterConfig',
14 'click #header-config': 'showHeaderConfig'
14 'click #header-config': 'showHeaderConfig',
15 'click #uploads': 'uploads'
1516 },
1617 initialize: function() {
1718 _.bindAll.apply(_, [this].concat(_.functions(this)));
3131 this.footerconfigview = new FooterConfigView({model: this.footerconfig});
3232 this.headerconfig = new M.types.model.header(M.site_content.header);
3333 this.headerconfigview = new HeaderConfigView({model: this.headerconfig});
34 this.uploadview = new UploadView();
3435 },
3536 render: function() {
3637 // append the page list
104104 this.headerconfigview.render();
105105 return false;
106106 },
107 uploads: function(event) {
108 event.preventDefault();
109 this.uploadview.render();
110 return false;
111 },
107112 // validate the page list with menu order list
108113 validate: function() {
109114 //TODO: validate if the menu order list matches with the list of pages
676676 ' will be rendered'+
677677 '\n@delay: (optional) a delay time, after which the notification'+
678678 ' will be hidden';
679 }
680 });
681
682 /* Upload View */
683 var UploadView = Backbone.View.extend({
684 tagName: 'div',
685 className: 'prettybox-lg',
686 id: 'page',
687 events: {
688 'click #upload-new-file': 'uploadFile',
689 'click .uploaded-item .remove': 'removeFile'
690 },
691 initialize: function() {
692 _.bindAll.apply(_, [this].concat(_.functions(this)));
693 this.template = _.template($('#uploads-template').html());
694 },
695 render: function() {
696 $('#page').remove();
697 $('#content-container').append(this.$el);
698 //console.log('rendering..', this.$el);
699 var uploaded_files, self = this;
700 M.editor.showOverlay();
701 $.ajax({
702 url: '/upload',
703 type: 'GET',
704 success: function(data) {
705 M.editor.hideOverlay();
706 self.$el.html(self.template({
707 }));
708 self.appendFileListTemplate(data.uploaded_files);
709 self.delegateEvents();
710 }
711 });
712 },
713 appendFileListTemplate: function(files) {
714 var template = _.template($('#uploaded-item-template').html());
715 if(files.length) {
716 _.each(files, function(file) {
717 $('#uploads-list').append(template({
718 filename: file
719 }));
720 });
721 }
722 else {
723 $('#uploads-list').html('<b> No files uploaded yet </b>');
724 }
725 },
726 uploadFile: function() {
727 //console.log('upload file');
728 var self = this;
729 M.editor.showOverlay();
730 var $form = $('#file-upload-form')[0];
731 var formdata = new FormData($form);
732 $.ajax({
733 type: 'POST',
734 url: '/upload',
735 data: formdata,
736 processData: false,
737 contentType: false,
738 success: function(response) {
739 M.editor.hideOverlay();
740 M.editor.notifs.show('success', 'Success', 'File uploaded');
741 self.render();
742 },
743 error: function(jqxhr, status, error) {
744 M.editor.hideOverlay();
745 if(error === 'BAD REQUEST') {
746 var msg = 'File format not allowed. Please contact your administrator to allow this kind of file.'
747 } else {
748 var msg = 'Something went wrong. Please try again!';
749 }
750 M.editor.notifs.show('fail', 'Error!', msg);
751 }
752 });
753 },
754 removeFile: function(event) {
755 M.editor.showOverlay();
756 //console.log('remove file');
757 var self = this;
758 var filename = $(event.currentTarget).attr('for');
759 $.ajax({
760 type: 'DELETE',
761 url: '/upload/' + filename,
762 success: function(data) {
763 M.editor.hideOverlay();
764 self.render();
765 },
766 error: function(jqxhr, status, error) {
767 console.log(arguments);
768 }
769 });
679770 }
680771 });
681772
  
2626 M.HeaderURL = function() { return "{{ url_for('insertHeader') }}"; };
2727 M.PageURL = function() { return "{{ url_for('insertPage') }}"; };
2828 M.PluginUploadURL = function() { return "{{ url_for('uploadPlugin') }}"; };
29 M.UploadsURL = function() { return "{{ url_for('static', filename='uploads/') }}"; };
2930 M.site_content = {{ content|tojson|safe }};
3031 window.onload = function() {
3132 M.editor.init();
144144 <p> <a href="#" id="header-config"> Header </a> </p>
145145 <p> <a href="#" id="footer-config"> Footer </a> </p>
146146 <p> <a href="#" id="menu-config"> Navigation Menu </a> </p>
147 <p> <a href="#" id="uploads"> Uploads </a> </p>
147148 <p><a href="{{ url_for('index') }}"> Go to site </a></p>
148149 </div>
149150 </script>
196196 <small>title of the page</small>
197197 </span>
198198 </div>
199 <div class="form-group">
199 <!--div class="form-group">
200200 <div class="input-group">
201201 <span class="input-group-addon"> <strong>Children</strong> </span>
202202 <input class="form-control" id="children" type="text" placeholder="csv of child pages"
205205 <span class="help-block">
206206 <small> leave this blank for now</small>
207207 </span>
208 </div>
208 </div-->
209209 <div class="form-group">
210210 <div class="input-group">
211211 <span class="input-group-addon">
336336 <div class="row">
337337 <div class="form-group col-lg-6">
338338 <form id="plugin-upload-form" action="" method="post" enctype="multipart/form-data">
339 <input type="file" name="plugin-file" id="select-plugin">
339 <input type="file" name="plugin-file" id="select-plugin">
340340 <input type="button" id="upload-plugin" class="btn btn-info" value="Upload Plugin">
341341 </form>
342342 </div>
404404 <button type="button" class="close" data-dismiss="alert">&times;</button>
405405 <h4> <%= title %> </h4>
406406 <%= msg %>
407 </div>
408 </script>
409
410 <script type="text/template" id="uploads-template">
411 <div class="page">
412 <div><h4> All your uploads </h4></div>
413 <div id="uploads-list" class="well">
414 </div>
415 <hr>
416 <div class="row">
417 <div> <h4> New Upload </h4> </div>
418 <form id="file-upload-form" action="" method="post" enctype="multipart/form-data">
419 <input type="file" name="upload-file" id="select-file">
420 <input type="button" id="upload-new-file" class="btn btn-sm btn-primary update-btn" value="Upload File">
421 </form>
422 </div>
423 <div class="clearfix"></div>
424 </div>
425 </script>
426
427 <script type="text/template" id="uploaded-item-template">
428 <div class="uploaded-item row">
429 <span> &lt;icon here&gt; </span>
430 <!--span class="uploaded-item-name"> <%= filename %> </span-->
431 <span class="uploaded-item-path">
432 <a href="<%= M.UploadsURL() + filename %>">
433 <%= M.UploadsURL() + filename %>
434 </a>
435 </span>
436 <span class="pull-right">
437 <a href="javascript:void(0);" class="remove" for="<%= filename %>">
438 <span class="glyphicon glyphicon-trash"></span>
439 </a>
440 </span>
441 <span class="clearfix"></span>
407442 </div>
408443 </script>
409444