From 4e1af813c4647126c917e339c4d879362cefa339 Mon Sep 17 00:00:00 2001 From: Anon Ray Date: Mon, 7 Oct 2013 21:22:20 +0530 Subject: [PATCH] Ace editor for plugins Integrating ace editor for plugins One can upload plugins too. Create plugin is not yet implemented. All plugins have to be moved to static/user_plugins/ --- .gitignore | 4 ++++ mouchak/server.py | 46 +++++++++++++++++++++++++++++++++++++++++ mouchak/static/css/editor.css | 2 +- mouchak/static/js/editor.js | 45 +++++++++++++++++++++++++++++++++++++++- mouchak/static/js/models.js | 35 ++++++++++++++++++++++++++++++- mouchak/static/js/views.js | 2 +- mouchak/templates/editor.html | 29 +++++++++++++++++++++----- 7 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 mouchak/static/uploads/.empty create mode 100644 mouchak/static/user_plugins/.empty diff --git a/.gitignore b/.gitignore index 59db5ee..6748fd7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ conf.py /lib/ /local/ /include/ + +#uploads directory +mouchak/static/uploads/* +mouchak/static/user_plugins/* diff --git a/mouchak/server.py b/mouchak/server.py index dc19d1f..278053a 100644 --- a/mouchak/server.py +++ b/mouchak/server.py @@ -7,6 +7,15 @@ import flask import pymongo import bson import conf +import os +from werkzeug import secure_filename + +PLUGIN_UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)) + + '/static/user_plugins') +PLUGIN_ALLOWED_EXTENSIONS = set(['js', 'css']) + +FILE_UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)) + + '/static/uploads') app = flask.Flask(__name__) @@ -40,6 +49,11 @@ def getContent(): return {'content': content, 'menu': menu} +def allowed_file(filename): + return '.' in filename and \ + filename.rsplit('.', 1)[1] in PLUGIN_ALLOWED_EXTENSIONS + + @app.errorhandler(404) def pageNotFound(e): return flask.render_template('404.html'), 404 @@ -153,6 +167,37 @@ def logout(): flask.flash('You were logged out') return flask.redirect(flask.url_for('login')) + +#TODO: refactor these code to classes +#TODO: find out if this is a good method for saving plugins.. +@app.route('/static/user_plugins/', methods=['POST']) +def savePlugin(filename): + if flask.request.method == 'POST': + if filename and allowed_file(filename): + data = flask.request.form['code'] + filename = secure_filename(filename) + fh = open(os.path.join(PLUGIN_UPLOAD_FOLDER + '/' + filename), 'w') + fh.write(data) + fh.close() + return flask.jsonify(saved = True) + +#TODO: find out if this is a good method for uploading plugins.. +@app.route('/upload/plugin', methods=['POST']) +def uploadPlugin(): + if flask.request.method == 'POST': + print flask.request.files + file = flask.request.files['plugin-file'] + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['PLUGIN_UPLOAD_FOLDER'], + filename)) + + #return flask.redirect(flask.url_for('uploaded_file', + # filename=filename)) + return flask.jsonify(uploaded = True, + path=flask.url_for('static', filename = + 'user_plugins/'+ filename)) + @app.route('/robots.txt') @app.route('/crossdomain.xml') def static_from_root(): @@ -160,6 +205,7 @@ def static_from_root(): app.config.from_object(conf) +app.config['PLUGIN_UPLOAD_FOLDER'] = PLUGIN_UPLOAD_FOLDER import logging,os from logging import FileHandler diff --git a/mouchak/static/css/editor.css b/mouchak/static/css/editor.css index ca23566..594f076 100644 --- a/mouchak/static/css/editor.css +++ b/mouchak/static/css/editor.css @@ -73,7 +73,7 @@ body { left: 33%; top: 0px; } -#code-edit { +.ace-mouchak { position: relative; top: 0; left: 0; diff --git a/mouchak/static/js/editor.js b/mouchak/static/js/editor.js index 7e5c46a..0c52cf1 100644 --- a/mouchak/static/js/editor.js +++ b/mouchak/static/js/editor.js @@ -186,6 +186,7 @@ split('-')[1]; var content = this.model.get('content')[idx]; content = new M.types.model[content.type](content); + console.log('model inited ', content); this.editing = true; this.edit_idx = idx; var contentview = new ContentView({model: content}); @@ -299,7 +300,9 @@ 'click #updateContent': 'update', 'click #back' : 'back', 'click #edit-type button' : 'editTypeChanged', - 'change .contentview select': 'typeChanged' + 'change .contentview select': 'typeChanged', + /* plugin events */ + 'click #upload-plugin': 'uploadPlugin' }, initialize: function() { _.bindAll.apply(_, [this].concat(_.functions(this))); @@ -358,6 +361,14 @@ src: this.model.get('src'), callback: this.model.get('callback') })); + if(this.model.get('src')) { + var plugin_type = this.model.get('plugin_type'); + plugin_type = (plugin_type === 'js') ? 'javascript': 'css'; + this.model.getCode(function(data) { + $('#plugin-edit').html(escapeHtml(data)); + M.editor.code.init('plugin-edit', plugin_type); + }); + } } else if(type === 'map') { var template = _.template($('#map-template').html()); @@ -371,6 +382,10 @@ var type = this.$select.val(); //TODO: do validation on type - a list of valid models is in //M.types.model + var base_props = _.keys(new M.types.model.base().defaults); + var props = _.omit(this.model.toJSON(), base_props); + var new_model = new M.types.model[type](props); + this.model = new_model; this.model.set({'type': type}); this.render(); }, @@ -410,6 +425,12 @@ new_attrs['data'] = data; } } + else if(this.$select.val() === 'plugin') { + var data = M.editor.code.save('plugin-edit'); + this.model.saveCode(data, function(resp) { + console.log('plugin saved..'); + }); + } this.model.set(new_attrs); M.editor.pageview.updateContent(this.model.toJSON()); }, @@ -425,6 +446,28 @@ }, back: function() { this.cleanUp(); + }, + //upload inputed plugin file to server + uploadPlugin: function(event) { + var self = this; + M.editor.showOverlay(); + var $form = $('#plugin-upload-form')[0]; + console.log($form); + var formdata = new FormData($form); + console.log(formdata); + $.ajax({ + type: 'POST', + url: M.PluginUploadURL(), + data: formdata, + processData: false, + contentType: false, + success: function(response) { + self.model.set({'src': response.path}) + self.render(); + M.editor.hideOverlay(); + console.log(self.model.toJSON()); + } + }); } }); diff --git a/mouchak/static/js/models.js b/mouchak/static/js/models.js index c277bc0..b1f26cf 100644 --- a/mouchak/static/js/models.js +++ b/mouchak/static/js/models.js @@ -80,9 +80,18 @@ data: {}, callback: "" }, BaseType.prototype.defaults), + initialize: function() { BaseType.prototype.initialize.call(this, arguments); - + if(this.get('src').match(/\.js/)) { + this.set({'plugin_type': 'js'}); + } + else if(this.get('src').match(/\.css/)) { + this.set({'plugin_type': 'css'}); + } + }, + exec: function() { + console.log('exec called'); if(this.get('src').match(/\.js/)) { var script = document.createElement('script'); var callback = this.get('callback'); @@ -102,6 +111,30 @@ link.type = 'text/css'; document.body.appendChild(link); } + }, + // get the source code of the plugin from the src path + getCode: function(cb) { + var self = this; + $.ajax({ + type: 'GET', + url: self.get('src'), + cache: false, + success: function(data) { + cb(data); + } + }); + }, + // save the source code of the plugin to the src path + saveCode: function(data, cb) { + var self = this; + $.ajax({ + type: 'POST', + url: this.get('src'), + data: {code: data}, + success: function(data) { + cb(data); + } + }); } }); diff --git a/mouchak/static/js/views.js b/mouchak/static/js/views.js index 055ae8b..832d38d 100644 --- a/mouchak/static/js/views.js +++ b/mouchak/static/js/views.js @@ -106,7 +106,7 @@ return; }, render: function(el) { - return; + this.model.exec(); } }); diff --git a/mouchak/static/uploads/.empty b/mouchak/static/uploads/.empty new file mode 100644 index 0000000..e69de29 diff --git a/mouchak/static/user_plugins/.empty b/mouchak/static/user_plugins/.empty new file mode 100644 index 0000000..e69de29 diff --git a/mouchak/templates/editor.html b/mouchak/templates/editor.html index 8f56c2b..1df5a57 100644 --- a/mouchak/templates/editor.html +++ b/mouchak/templates/editor.html @@ -21,8 +21,9 @@ @@ -339,7 +358,7 @@ <%= data %> <% } else { %> -
+
<%= M.escapeHtml(data) %>
<% } %> -- 1.7.10.4