diff options
| -rw-r--r-- | public/assets/javascripts/ui/EditProfileModal.js | 51 | ||||
| -rw-r--r-- | public/assets/javascripts/ui/MasterView.js | 3 | ||||
| -rw-r--r-- | public/assets/javascripts/ui/ModalFormView.js | 70 | ||||
| -rw-r--r-- | public/assets/javascripts/ui/ModalView.js | 1 | ||||
| -rw-r--r-- | public/assets/javascripts/ui/NewProjectModal.js | 39 | ||||
| -rw-r--r-- | public/assets/javascripts/ui/SignInModal.js | 49 | ||||
| -rw-r--r-- | public/assets/javascripts/ui/SignUpModal.js | 60 | ||||
| -rw-r--r-- | server/index.js | 2 | ||||
| -rw-r--r-- | server/lib/api.js | 24 | ||||
| -rw-r--r-- | server/lib/auth.js | 6 | ||||
| -rw-r--r-- | server/lib/schemas/User.js | 8 | ||||
| -rw-r--r-- | server/lib/upload.js | 72 | ||||
| -rw-r--r-- | server/lib/util.js | 11 | ||||
| -rw-r--r-- | views/partials/edit-profile.ejs | 47 | ||||
| -rw-r--r-- | views/partials/scripts.ejs | 1 |
15 files changed, 248 insertions, 196 deletions
diff --git a/public/assets/javascripts/ui/EditProfileModal.js b/public/assets/javascripts/ui/EditProfileModal.js index de8a8be..5b79a31 100644 --- a/public/assets/javascripts/ui/EditProfileModal.js +++ b/public/assets/javascripts/ui/EditProfileModal.js @@ -1,48 +1,23 @@ -var EditProfileModal = ModalView.extend({ +var EditProfileModal = ModalFormView.extend({ el: ".mediaDrawer.editProfile", - action: "/project/edit", + action: "/api/profile", - events: { - "submit form": "submit" - }, - - initialize: function(){ - this.$form = this.$("form") - this.$errors = this.$(".errors") - this.$errorList = this.$(".errorList") - }, - - reset: function(){ - this.$("input").not("[type='submit']").not("[type='hidden']").val("") - }, - load: function(){ this.reset() - this.show() + $.get("/api/profile", $.proxy(function(data){ + console.log(data) + + for (var i in data) { + this.$("[name='" + i + "']").val(data[i]) + } + + this.show() + }, this)) }, - submit: function(e){ - e.preventDefault() - - this.$errors.hide(); - this.$errorList.empty() - - var fields = this.$form.serializeArray() - - var request = $.post(this.action, $.param(fields)); - request.done($.proxy(function (response) { - if (response.error) { - this.$errors.show(); - for (var key in response.error.errors) { - this.$errorList.append('<div>' + response.error.errors[key].message + '</div>'); - } - return - } - else { - window.location.href = "/profile" - } - }, this)); + success: function(){ + window.location.href = "/profile" } }) diff --git a/public/assets/javascripts/ui/MasterView.js b/public/assets/javascripts/ui/MasterView.js index a39c6f0..5aca4e0 100644 --- a/public/assets/javascripts/ui/MasterView.js +++ b/public/assets/javascripts/ui/MasterView.js @@ -25,6 +25,8 @@ var MasterView = View.extend({ this.editProjectModal = new EditProjectModal() this.editProfileModal = new EditProfileModal() + this.originalPath = window.location.pathname + for (var route in this.routes) { if (window.location.pathname.indexOf(route) === 0) { this[this.routes[route]]() @@ -61,7 +63,6 @@ var MasterView = View.extend({ editProject: function(e){ e && e.preventDefault() window.history.pushState(null, document.title, "/project/edit") - this.editProjectModal.load() }, diff --git a/public/assets/javascripts/ui/ModalFormView.js b/public/assets/javascripts/ui/ModalFormView.js new file mode 100644 index 0000000..608b8c1 --- /dev/null +++ b/public/assets/javascripts/ui/ModalFormView.js @@ -0,0 +1,70 @@ + +var ModalFormView = ModalView.extend({ + + events: { + "submit form": "submit" + }, + + initialize: function(){ + this.$form = this.$("form") + this.$errors = this.$(".errors") + this.$errorList = this.$(".errorList") + }, + + reset: function(){ + this.$("input").not("[type='submit']").not("[type='hidden']").val("") + }, + + load: function(){ + this.reset() + this.show() + }, + + showErrors: function(errors){ + if (errors && errors.length) { + this.$errors.show(); + for (var i in errors) { + this.$errorList.append('<div>' + errors[i] + '</div>'); + } + } + }, + + submit: function(e){ + e.preventDefault() + + this.$errors.hide(); + this.$errorList.empty(); + + if (this.validate) { + var errors = this.validate() + if (errors && errors.length) { + this.showErrors(errors) + return + } + } + + var fields = this.$form.serializeArray() + fields.forEach(function(pair){ + if (pair.name == "password" && pair.value.length > 0) { + pair.value = SHA1.hex('lol$' + pair.value + '$vvalls') + } + }) + + var request = $.post(this.action, $.param(fields)); + request.done($.proxy(function (response) { + if (response.error) { + this.$errors.show(); + var errors = [] + for (var key in response.error.errors) { + errors.push(response.error.errors[key].message); + } + this.showErrors(errors) + return + } + else { + this.success && this.success(response) + } + }, this)); + } + +}) diff --git a/public/assets/javascripts/ui/ModalView.js b/public/assets/javascripts/ui/ModalView.js index a34520f..80ce8d0 100644 --- a/public/assets/javascripts/ui/ModalView.js +++ b/public/assets/javascripts/ui/ModalView.js @@ -21,6 +21,7 @@ var ModalView = View.extend({ window.location.pathname = "/" } else { + history.pushState(null, document.title, app.master.originalPath) this.hide() } } diff --git a/public/assets/javascripts/ui/NewProjectModal.js b/public/assets/javascripts/ui/NewProjectModal.js index 58dcb73..cf2044f 100644 --- a/public/assets/javascripts/ui/NewProjectModal.js +++ b/public/assets/javascripts/ui/NewProjectModal.js @@ -1,49 +1,16 @@ -var NewProjectModal = ModalView.extend({ +var NewProjectModal = ModalFormView.extend({ el: ".mediaDrawer.newProject", action: "/project/new", - events: { - "submit form": "submit" - }, - - initialize: function(){ - this.$form = this.$("form") - this.$errors = this.$(".errors") - this.$errorList = this.$(".errorList") - }, - - reset: function(){ - this.$("input").not("[type='submit']").not("[type='hidden']").val("") - }, - load: function(){ this.reset() this.show() }, - submit: function(e){ - e.preventDefault() - - this.$errors.hide(); - this.$errorList.empty() - - var fields = this.$form.serializeArray() - - var request = $.post(this.action, $.param(fields)); - request.done($.proxy(function (response) { - if (response.error) { - this.$errors.show(); - for (var key in response.error.errors) { - this.$errorList.append('<div>' + response.error.errors[key].message + '</div>'); - } - return - } - else { - window.location.href = "/profile" - } - }, this)); + success: function(){ + // } }) diff --git a/public/assets/javascripts/ui/SignInModal.js b/public/assets/javascripts/ui/SignInModal.js index 0112513..4c91b54 100644 --- a/public/assets/javascripts/ui/SignInModal.js +++ b/public/assets/javascripts/ui/SignInModal.js @@ -1,54 +1,11 @@ -var SignInModal = ModalView.extend({ +var SignInModal = ModalFormView.extend({ el: ".mediaDrawer.signin", action: "/auth/signin", - events: { - "submit form": "submit" - }, - - initialize: function(){ - this.$form = this.$("form") - this.$errors = this.$(".errors") - this.$errorList = this.$(".errorList") - }, - - reset: function(){ - this.$("input").not("[type='submit']").not("[type='hidden']").val("") - }, - - load: function(){ - this.reset() - this.show() - }, - - submit: function(e){ - e.preventDefault() - - this.$errors.hide(); - this.$errorList.empty() - - var fields = this.$form.serializeArray() - fields.forEach(function(pair){ - if (pair.name == "password" && pair.value.length > 0) { - pair.value = SHA1.hex('lol$' + pair.value + '$vvalls') - } - }) - - var request = $.post(this.action, $.param(fields)); - request.done($.proxy(function (response) { - if (response.error) { - this.$errors.show(); - for (var key in response.error.errors) { - this.$errorList.append('<div>' + response.error.errors[key].message + '</div>'); - } - return - } - else { - window.location.href = "/profile" - } - }, this)); + success: function(res){ + window.location.href = "/profile" } }) diff --git a/public/assets/javascripts/ui/SignUpModal.js b/public/assets/javascripts/ui/SignUpModal.js index 160323b..95b5837 100644 --- a/public/assets/javascripts/ui/SignUpModal.js +++ b/public/assets/javascripts/ui/SignUpModal.js @@ -1,28 +1,9 @@ -var SignUpModal = ModalView.extend({ +var SignUpModal = ModalFormView.extend({ el: ".mediaDrawer.signup", action: "/auth/signup", - - events: { - "submit form": "submit", - }, - - initialize: function(){ - this.$form = this.$("form") - this.$errors = this.$(".errors") - this.$errorList = this.$(".errorList") - }, - reset: function(){ - this.$("input").not("[type='submit']").not("[type='hidden']").val("") - }, - - load: function(){ - this.reset() - this.show() - }, - validate: function(){ var errors = [] @@ -47,43 +28,12 @@ var SignUpModal = ModalView.extend({ errors.push("Passwords don't match"); } - if (errors.length) { - this.$errors.show(); - for (var i in errors) { - this.$errorList.append('<div>' + errors[i] + '</div>'); - } - } - - return ! errors.length + return errors }, - submit: function(e){ - e.preventDefault() - - this.$errors.hide(); - this.$errorList.empty() - - if (! this.validate()) return - - var fields = this.$form.serializeArray() - fields.forEach(function(pair){ - if (pair.name == "password" && pair.value.length > 0) { - pair.value = SHA1.hex('lol$' + pair.value + '$vvalls') - } - }) - var request = $.post(this.action, $.param(fields)); - request.done($.proxy(function (response) { - if (response.error) { - this.$errors.show(); - for (var key in response.error.errors) { - this.$errorList.append('<div>' + response.error.errors[key].message + '</div>'); - } - return; - } - else { - window.location.href = "/profile" - } - }, this)); + success: function(res){ + window.location.href = "/profile" } }) + diff --git a/server/index.js b/server/index.js index fc77660..34d5989 100644 --- a/server/index.js +++ b/server/index.js @@ -71,6 +71,8 @@ app.get('/auth/facebook', auth.login('facebook')); app.get('/auth/facebook/callback', auth.loggedIn('facebook')); app.get('/profile', views.profile) app.get('/profile/edit', views.profile) +app.get('/api/profile', middleware.ensureAuthenticated, api.profile.show) +app.put('/api/profile', middleware.ensureAuthenticated, api.profile.update) app.get('/project/new', views.modal); diff --git a/server/lib/api.js b/server/lib/api.js index d9934ab..9ff9f38 100644 --- a/server/lib/api.js +++ b/server/lib/api.js @@ -3,10 +3,32 @@ var passport = require('passport'), _ = require('lodash'), Entities = require('html-entities').XmlEntities, - entities = new Entities(); + entities = new Entities(), + crypto = require('crypto'), + _ = require('lodash'), + util = require('./util'), + config = require('../../config.json'), + User = require('./schemas/User'); var api = { + + profile: { + show: function(req, res){ + User.findOne({ _id: req.user._id }, function(err, user){ + res.json(err || user) + }) + }, + update: function(req, res){ + var data = req.cleanQuery(req.body) + if (data.new_password.length && req.user.checkPassword(data.old_password)) { + data.password = data.new_password + } + delete data.old_password + delete data.new_password + } + } + } diff --git a/server/lib/auth.js b/server/lib/auth.js index ede52b6..e7b7a75 100644 --- a/server/lib/auth.js +++ b/server/lib/auth.js @@ -5,6 +5,7 @@ var passport = require('passport'), TwitterStrategy = require('passport-twitter').Strategy, LocalStrategy = require('passport-local').Strategy, passportSocketIo = require("passport.socketio"), + crypto = require('crypto'), _ = require('lodash'), util = require('./util'), config = require('../../config.json'), @@ -91,6 +92,10 @@ var auth = { var username = util.trim(req.body.username) var password = req.body.password var email = util.trim(req.body.email) + + var shasum = crypto.createHash('sha1') + shasum.update(password) + password = shasum.digest('hex'); User.findOne({ username: username }, function (err, user) { if (user) { @@ -156,6 +161,7 @@ var auth = { username: profile.username || profile.displayName.toLowerCase().replace(/ /g,'-'), displayName: profile.displayName, photo: "http://graph.facebook.com/" + profile.id + "/picture?type=large", + facebookUrl: profile.username ? "https://facebook.com/" + profile.username : "" }; User.findOne({facebook_id: profile.id}, function(err, data){ diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index d78bfd2..24b0adf 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -4,6 +4,7 @@ var NONALPHANUMERICS_REGEX = new RegExp('[^-_a-zA-Z0-9]', 'g') var mongoose = require('mongoose'), _ = require('lodash'), + crypto = require('crypto'), config = require('../../../config.json'); var UserSchema = new mongoose.Schema({ @@ -28,6 +29,8 @@ var UserSchema = new mongoose.Schema({ case 'assets': case 'admin': case 'terms': + case 'api': + case 'vvalls': case 'assets': case '': return false @@ -53,11 +56,14 @@ var UserSchema = new mongoose.Schema({ bio: { type: String, default: "" }, website: { type: String, default: "" }, twitterName: { type: String, default: "" }, + facebookUrl: { type: String, default: "" }, isAdmin: { type: Boolean, default: false } }); UserSchema.methods.validPassword = function (pw) { - return this.password === pw + var shasum = crypto.createHash('sha1') + shasum.update(pw) + return this.password === shasum.digest('hex'); } module.exports = exports = mongoose.model('user', UserSchema); diff --git a/server/lib/upload.js b/server/lib/upload.js new file mode 100644 index 0000000..5c130a2 --- /dev/null +++ b/server/lib/upload.js @@ -0,0 +1,72 @@ + +var config = require('../../config.json'), + util = require('./util'), + knox = require('knox'), + moment = require('moment'); + +var s3 = module.exports.s3 = knox.createClient({ + key: process.env.OKFOCUS_S3_KEY, + secret: process.env.OKFOCUS_S3_SECRET, + bucket: 'vvalls' +}); + +var acceptableuploadTypes = { + 'image/gif': 'gif', + 'image/jpeg': 'jpg', + 'image/png': 'png' +} + +module.exports.put = function (key, image, opt) { + var imageSize, imageType, filename + var err + var now = new Date() + + var ts = moment().format('YYYYMMDD') + + var extension = acceptableuploadTypes[image.type] + filename = (+now) + "-" + + image.name.replace(/\..*$/,"") + .replace(/[^0-9a-zA-Z]+/g,"-") + .substr(-64) + + "." + extension; + + var remote_path = "/images/" + key + "/" + ts + "/" + filename + + if (! extension) { + err = "unacceptable filetype" + } + else if (image.size < 10) { + err = "file too small" + } + else if (image.size > 2097152) { // 2mb limit + err = "file too large" + } + + if (err) { + console.error(">>>", err) + opt.unacceptable && opt.unacceptable(err) + return + } + + opt.acceptable && opt.acceptable(err) + + console.log("upload > ", remote_path) + s3.putFile(image.path, remote_path, { + 'Content-Length': image.size, + 'Content-Type': image.type, + 'x-amz-acl': 'public-read' + }, function(err, s3res) { + if (err || s3res.statusCode !== 200) { + console.error(err); + s3res.resume() + return; + } + + var image_url = s3res.url || s3res.req.url + + opt.success && opt.success(image_url) + }).on('error', function(err, s3res){ + console.error(err) + s3res && s3res.resume && s3res.resume() + }) +} diff --git a/server/lib/util.js b/server/lib/util.js index 45902f3..7a63507 100644 --- a/server/lib/util.js +++ b/server/lib/util.js @@ -5,4 +5,15 @@ var whitespaceTail = /\s+$/ var util = {} util.trim = function (s){ return s.replace(whitespaceHead,"").replace(whitespaceTail,"") } +util.cleanQuery = function (query) { + var update = _.extend({}, query); + delete update._id; + delete update.created_at; + delete update.modified_at; + delete update.modified_by; + delete update.created_by; + return update; +} + + module.exports = util diff --git a/views/partials/edit-profile.ejs b/views/partials/edit-profile.ejs index d7c9a34..a9a5a9a 100644 --- a/views/partials/edit-profile.ejs +++ b/views/partials/edit-profile.ejs @@ -9,60 +9,71 @@ <h3>Edit Profile</h3> </li> <li> - <label class="description" for="element_3">Name:</label> + <label class="description" for="profile_displayName">Name:</label> <div> - <input id="element_3_1" name= "element_3_1" class="element text" type="text" maxlength="255" value="Ivan Sidorov"/> + <input id="profile_displayName" name="displayName" class="element text" type="text" maxlength="255"> </div> </li> <li> - <label class="description" for="element_2">Website:</label> + <label class="description" for="profile_email">Email:</label> <div> - <input id="element_2" name="element_2" class="element text medium" type="text" maxlength="255" value="http://"/> + <input id="profile_email" name="email" class="element text medium" type="text" maxlength="255"> </div> - </li> + </li> <li> - <label class="description" for="element_5">Twitter:</label> + <label class="description" for="profile_website">Website:</label> <div> - <input id="element_5" name="element_5" class="element text medium" type="text" maxlength="255" value="@"/> + <input id="profile_website" name="website" class="element text medium" type="text" maxlength="255"> + </div> + </li> + <li> + <label class="description" for="profile_twitter">Twitter:</label> + <div> + <input id="profile_twitter" name="twitter" class="element text medium" type="text" maxlength="255"> </div> </li> <li> - <label class="description" for="element_4">Facebook:</label> + <label class="description" for="profile_facebook">Facebook:</label> <div> - <input id="element_4" name="element_4" class="element text medium" type="text" maxlength="255" value="http://"/> + <input id="profile_facebook" name="facebook" class="element text medium" type="text" maxlength="255"> </div> </li> <li> - <label class="description" for="element_1">Upload Avatar:</label> + <label class="description" for="profile_avatar">Upload Avatar:</label> <div> - <input id="element_1" name="element_1" class="element file" type="file"/> - </div> <p class="guidelines" id="guide_1"><small>please choose a picture at least 500px wide</small></p> + <input id="profile_avatar" name="avatar" class="element file" type="file"/> + </div> + <p class="guidelines" id="guide_1"><small>please choose a picture at least 500px wide</small></p> </li> <li class="section_break"> <h3>Edit Password</h3> </li> <li> - <label class="description" for="element_7">Old Password:</label> + <label class="description" for="profile_old_password">Old Password:</label> <div> - <input id="element_7" name="element_7" class="element text medium" type="password" maxlength="255" value=""/> + <input id="profile_old_password" name="old_password" class="element text medium" type="password" maxlength="255"> </div> </li> <li> - <label class="description" for="element_8">New Password:</label> + <label class="description" for="profile_password">New Password:</label> <div> - <input id="element_8" name="element_8" class="element text medium" type="password" maxlength="255" value=""/> + <input id="profile_password" name="new_password" class="element text medium" type="password" maxlength="255"> </div> </li> <li> - <label class="description" for="element_9">Again!</label> + <label class="description" for="profile_new_password2">Again!</label> <div> - <input id="element_9" name="element_9" class="element text medium" type="password" maxlength="255" value=""/> + <input id="profile_new_password2" class="element text medium" type="password" maxlength="255"> </div> </li> <li class="buttons"> <input id="saveForm" class="button_text" type="submit" name="submit" value="Submit" /> </li> + <div class="errors"> + <div>There was a problem with your submission:</div> + <div class="errorList"></div> + </div> </ul> </form> </div> diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index e466f8b..fdb2229 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -44,6 +44,7 @@ <script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.image.js"></script> <script type="text/javascript" src="/assets/javascripts/ui/ModalView.js"></script> +<script type="text/javascript" src="/assets/javascripts/ui/ModalFormView.js"></script> <script type="text/javascript" src="/assets/javascripts/ui/MasterView.js"></script> <script type="text/javascript" src="/assets/javascripts/ui/SignInModal.js"></script> <script type="text/javascript" src="/assets/javascripts/ui/SignUpModal.js"></script> |
