From 927a3d0aa79c9b6c03b0fd628e7dde0bb703a195 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 4 Feb 2013 17:27:27 -0500 Subject: [PATCH 01/31] starting big migration --- .../20230204_user_public_private_paths.js | 56 ++++++++++++++++++ server.js | 2 +- src/app/schema.coffee | 57 ++++++++++--------- 3 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 migrations/20230204_user_public_private_paths.js diff --git a/migrations/20230204_user_public_private_paths.js b/migrations/20230204_user_public_private_paths.js new file mode 100644 index 0000000000..d7c0e23df6 --- /dev/null +++ b/migrations/20230204_user_public_private_paths.js @@ -0,0 +1,56 @@ +db.users.find({}).forEach(function(user){ + + // New user schema has public and private paths, so we can setup proper access control with racer + // Note 'public' and 'private' are reserved words + var newUser = { + pub:{}, + priv: {} + }; + + newUser.priv.lastCron = user.lastCron; + user.priv.balance = user.balance; + user.priv.tasks = user.tasks; + + // ------------ Stats + // rename money to gp + user.stats.gp = user.stats.money; + delete user.stats.money; + newUser.pub.stats = user.stats; + + // ------------ Party + newUser.pub.party = null; + + + // ------------ ID lists + newUser.priv.idLists = {habit:user.habitList, daily:user.dailyList, todo:user.todoList, reward:user.rewardList}; + + + // ------------ Flags + newUser.priv.flags = user.flags || {}; + + user.priv.partyEnabled = false; + + user.priv.flags.itemsEnabled = user.items.itemsEnabled; + delete user.items.itemsEnabled; + + // Items + user.pub.items = user.items; + + // kickstarter + if (!user.notifications || !user.notifications.kickstarter) user.notifications = {kickstarter:'show'} + newUser.priv.flags.kickstarter = user.notifications.kickstarter; + + // Ads + newUser.priv.flags.ads = user.flags.ads; + + // ------------ API Token + newUser.priv.apiToken = user.preferences.api_token; + delete user.preferences.api_token; + + // ------------ Preferences + newUser.pub.preferences = user.preferences; + + + + db.users.update({_id:user.id}, newUser, {multi:true}); +}) \ No newline at end of file diff --git a/server.js b/server.js index 83a79584d9..3a4edb0e0d 100644 --- a/server.js +++ b/server.js @@ -4,7 +4,7 @@ process.on('uncaughtException', function (exception) { }); require('coffee-script') // remove intermediate compilation requirement -require('./src/server').listen(process.env.PORT || 3000); +require('./lib/server').listen(process.env.PORT || 3000); // Note: removed "up" module, which is default for development (but interferes with and production + PaaS) // Restore to 5310bb0 if I want it back (see https://github.com/codeparty/derby/issues/165#issuecomment-10405693) diff --git a/src/app/schema.coffee b/src/app/schema.coffee index d8f601cdb1..942c0002a8 100644 --- a/src/app/schema.coffee +++ b/src/app/schema.coffee @@ -5,48 +5,51 @@ lodash = require 'lodash' derby = require 'derby' userSchema = - lastCron: 'new' #this will be replaced with `+new Date` on first run - balance: 2 - stats: { money: 0, exp: 0, lvl: 1, hp: 50 } - items: { itemsEnabled: false, armor: 0, weapon: 0 } - notifications: { kickstarter: 'show' } - preferences: { gender: 'm', armorSet: 'v1' } - flags: { partyEnabled: false } - party: [] - tasks: {} - habitIds: [] - dailyIds: [] - todoIds: [] - rewardIds: [] + # _id + pub: + stats: { gp: 0, exp: 0, lvl: 1, hp: 50 } + # party: null + # TODO invitations + items: { armor: 0, weapon: 0 } + preferences: { gender: 'm', armorSet: 'v1' } + priv: + idLists: + habit: [] + daily: [] + todo: [] + reward: [] + apiToken: null # set in newUserObject below + lastCron: 'new' #this will be replaced with `+new Date` on first run + balance: 2 + tasks: {} + flags: + partyEnabled: false + itemsEnabled: false + kickstarter: 'show' + # ads: 'show' # added on registration module.exports.newUserObject = -> # deep clone, else further new users get duplicate objects newUser = require('lodash').cloneDeep userSchema + newUser.priv.apiToken = derby.uuid() for task in content.defaultTasks - guid = task.id = require('racer').uuid() + guid = task.id = derby.uuid() newUser.tasks[guid] = task switch task.type - when 'habit' then newUser.habitIds.push guid - when 'daily' then newUser.dailyIds.push guid - when 'todo' then newUser.todoIds.push guid - when 'reward' then newUser.rewardIds.push guid + when 'habit' then newUser.priv.idLists.habit.push guid + when 'daily' then newUser.priv.idLists.daily.push guid + when 'todo' then newUser.priv.idLists.todo.push guid + when 'reward' then newUser.priv.idLists.reward.push guid return newUser module.exports.updateUser = (batch) -> user = batch.user + obj = user.batch.obj() - batch.set('notifications.kickstarter', 'show') unless user.get('notifications.kickstarter') - batch.set('party', []) unless !_.isEmpty(user.get('party')) - - # Preferences, including API key - # Some side-stepping to avoid unecessary set (one day, model.update... one day..) - currentPrefs = _.clone user.get('preferences') - mergedPrefs = _.defaults currentPrefs, { gender: 'm', armorSet: 'v1', api_token: derby.uuid() } - batch.set('preferences', mergedPrefs) + batch.set('priv.apiToken', derby.uuid()) unless obj.priv.apiToken ## Task List Cleanup # FIXME temporary hack to fix lists (Need to figure out why these are happening) - # FIXME consolidate these all under user.listIds so we can set them en-masse tasks = user.get('tasks') _.each ['habit','daily','todo','reward'], (type) -> path = "#{type}Ids" From 629ba2df7992c16db0f2c393144482299e66c731 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 4 Feb 2013 20:12:48 -0500 Subject: [PATCH 02/31] fix up migration to only save clean fields --- .../20230204_user_public_private_paths.js | 141 ++++++++++++------ src/app/schema.coffee | 4 +- 2 files changed, 94 insertions(+), 51 deletions(-) diff --git a/migrations/20230204_user_public_private_paths.js b/migrations/20230204_user_public_private_paths.js index d7c0e23df6..66d080c19b 100644 --- a/migrations/20230204_user_public_private_paths.js +++ b/migrations/20230204_user_public_private_paths.js @@ -1,56 +1,99 @@ -db.users.find({}).forEach(function(user){ +// %mongo server:27017/dbname underscore.js my_commands.js +// %mongo server:27017/dbname underscore.js --shell + +//db.users.find({'auth.facebook.email': 'tylerrenelle@gmail.com'}).forEach(function(user){ +db.users.find().forEach(function(user){ + + if (!user._id) { + print("User has null _id"); + return; // need to figure out how to delete these buggers if they don't have an id to delete from + } + + if (user._id.indexOf("$") === 0) { + print("User id starts with $ (" + user._id + ")") + return; + } + + // even though we're clobbering user later, sometimes these are undefined and crash the script + // this saves us some ternaries + user.stats = user.stats || {}; + user.items = user.items || {}; + user.preferences = user.preferences || {}; + user.notifications = user.notifications || {}; + user.flags = user.flags || {}; + user.habitIds = user.habitIds || []; + user.dailyIds = user.dailyIds || []; + user.todoIds = user.todoIds || []; + user.rewardIds = user.rewardIds|| []; + + _.each(user.tasks, function(task, key){ + if (!task.type) { + delete user.tasks[key]; + // idList will take care of itself on page-load + return + } + if (key == '$spec') { + print("$spec was found: " + user._id); + return + } + if (key.indexOf("$_") === 0) { + var newKey = key.replace("$_", ''), + index = user[task.type + "Ids"].indexOf(key) + user[task.type + "Ids"][index] = newKey; + task.id = newKey + user.tasks[newKey] = task + // TODO make sure this is ok, that we're not deleting the original + // Otherwise use lodash.cloneDeep + delete user.tasks[key] + } + }); // New user schema has public and private paths, so we can setup proper access control with racer // Note 'public' and 'private' are reserved words var newUser = { - pub:{}, - priv: {} + auth: user.auth, // we need this top-level due to derby-auth + pub:{ + party: null, + invitations: [], + items: { + armor: user.items.armor || 0, + weapon: user.items.weapon || 0 + }, + stats: { + gp: user.stats.money || 0, + hp: user.stats.hp || 50, + exp: user.stats.exp || 0, + lvl: user.stats.lvl || 1 + }, + preferences: { + armorSet: user.preferences.armorSet || 'v1', + gender: user.preferences.gender || 'm' + } + }, + priv: { + balance: user.balance || 2, + lastCron: user.lastCron || +new Date, + tasks: user.tasks || {}, + history: user.history || [], + apiToken: user.preferences.api_token || null, // set on update, we need derby.uuid() + idLists: { + habit:user.habitList || [], + daily:user.dailyList || [], + todo:user.todoList || [], + reward:user.rewardList || [] + }, + flags: { + partyEnabled: false, + itemsEnabled: user.items.itemsEnabled || false, + kickstarter: user.notifications.kickstarter || 'show', + ads: user.flags.ads || null // null because it's set on registration + } + } }; - newUser.priv.lastCron = user.lastCron; - user.priv.balance = user.balance; - user.priv.tasks = user.tasks; - - // ------------ Stats - // rename money to gp - user.stats.gp = user.stats.money; - delete user.stats.money; - newUser.pub.stats = user.stats; - - // ------------ Party - newUser.pub.party = null; - - - // ------------ ID lists - newUser.priv.idLists = {habit:user.habitList, daily:user.dailyList, todo:user.todoList, reward:user.rewardList}; - - - // ------------ Flags - newUser.priv.flags = user.flags || {}; - - user.priv.partyEnabled = false; - - user.priv.flags.itemsEnabled = user.items.itemsEnabled; - delete user.items.itemsEnabled; - - // Items - user.pub.items = user.items; - - // kickstarter - if (!user.notifications || !user.notifications.kickstarter) user.notifications = {kickstarter:'show'} - newUser.priv.flags.kickstarter = user.notifications.kickstarter; - - // Ads - newUser.priv.flags.ads = user.flags.ads; - - // ------------ API Token - newUser.priv.apiToken = user.preferences.api_token; - delete user.preferences.api_token; - - // ------------ Preferences - newUser.pub.preferences = user.preferences; - - - - db.users.update({_id:user.id}, newUser, {multi:true}); + try { + db.users.update({_id:user._id}, newUser); + } catch(e) { + print(e); + } }) \ No newline at end of file diff --git a/src/app/schema.coffee b/src/app/schema.coffee index 942c0002a8..71012b2f94 100644 --- a/src/app/schema.coffee +++ b/src/app/schema.coffee @@ -8,8 +8,8 @@ userSchema = # _id pub: stats: { gp: 0, exp: 0, lvl: 1, hp: 50 } - # party: null - # TODO invitations + party: null + invitations: [] items: { armor: 0, weapon: 0 } preferences: { gender: 'm', armorSet: 'v1' } priv: From 96338f3169ee2f6b29e6cfe8f600bce2f15174d0 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 4 Feb 2013 21:13:04 -0500 Subject: [PATCH 03/31] allow crash server --- server.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server.js b/server.js index 3a4edb0e0d..51b8a7527c 100644 --- a/server.js +++ b/server.js @@ -1,10 +1,9 @@ -process.on('uncaughtException', function (exception) { - console.error(exception); - // don't crash for now -}); +/*process.on('uncaughtException', function (error) { + console.log(error.stack); +});*/ require('coffee-script') // remove intermediate compilation requirement -require('./lib/server').listen(process.env.PORT || 3000); +require('./src/server').listen(process.env.PORT || 3000); // Note: removed "up" module, which is default for development (but interferes with and production + PaaS) // Restore to 5310bb0 if I want it back (see https://github.com/codeparty/derby/issues/165#issuecomment-10405693) From 6966f6a439f0df3dffb7a8e5664bac7059a6f54c Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 4 Feb 2013 21:13:19 -0500 Subject: [PATCH 04/31] modify everything to accomdate migratino --- src/app/browser.coffee | 14 ++--- src/app/helpers.coffee | 11 ++-- src/app/index.coffee | 86 ++++++++++++++-------------- src/app/schema.coffee | 18 +++--- src/app/scoring.coffee | 100 ++++++++++++++++----------------- src/server/cron.coffee | 4 +- src/server/private.coffee | 9 ++- src/server/serverRoutes.coffee | 4 +- views/app/index.html | 48 ++++++++-------- 9 files changed, 147 insertions(+), 147 deletions(-) diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 6e302ddeef..28ec5ac4d3 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -85,7 +85,7 @@ module.exports.setupGrowlNotifications = (model) -> statsNotification = (html, type) -> #don't show notifications if user dead - return if user.get('stats.lvl') == 0 + return if user.get('pub.stats.lvl') == 0 $.bootstrapGrowl html, type: type # (null, 'info', 'error', 'success') top_offset: 20 @@ -95,7 +95,7 @@ module.exports.setupGrowlNotifications = (model) -> allow_dismiss: true stackup_spacing: 10 # spacing between consecutive stacecked growls. - user.on 'set', 'items.itemsEnabled', (captures, args) -> + user.on 'set', 'priv.flags.itemsEnabled', (captures, args) -> return unless captures == true message = "Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information." $('ul.items').popover @@ -109,7 +109,7 @@ module.exports.setupGrowlNotifications = (model) -> " $('ul.items').popover 'show' - user.on 'set', 'flags.partyEnabled', (captures, args) -> + user.on 'set', 'priv.flags.partyEnabled', (captures, args) -> return unless captures == true message = "Congratulations, you have unlocked the Party System! You can now group with your friends by adding their User Ids." $('#add-party-button').popover @@ -125,24 +125,24 @@ module.exports.setupGrowlNotifications = (model) -> # Setup listeners which trigger notifications - user.on 'set', 'stats.hp', (captures, args) -> + user.on 'set', 'pub.stats.hp', (captures, args) -> num = captures - args rounded = Math.abs(num.toFixed(1)) if num < 0 statsNotification "HP -#{rounded}", 'error' # lost hp from purchase - user.on 'set', 'stats.money', (captures, args) -> + user.on 'set', 'pub.stats.gp', (captures, args) -> num = captures - args rounded = Math.abs(num.toFixed(1)) # made purchase if num < 0 # FIXME use 'warning' when unchecking an accidently completed daily/todo, and notify of exp too statsNotification "GP -#{rounded}", 'success' - # gained money (and thereby exp) + # gained gp (and thereby exp) else if num > 0 num = Math.abs(num) statsNotification "Exp,GP +#{rounded}", 'success' - user.on 'set', 'stats.lvl', (captures, args) -> + user.on 'set', 'pub.stats.lvl', (captures, args) -> if captures > args statsNotification(' Level Up!', 'info') \ No newline at end of file diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index 1429b50168..99c90198b4 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -44,7 +44,7 @@ module.exports.viewHelpers = (view) -> else return "0" - view.fn "money", (num) -> + view.fn "gp", (num) -> if num return num.toFixed(2) else @@ -53,10 +53,13 @@ module.exports.viewHelpers = (view) -> view.fn "lessThan", (a, b) -> a < b - view.fn "tokens", (money) -> - return money/0.25 + view.fn "tokens", (gp) -> + return gp/0.25 - view.fn 'currentArmor', (gender, armor, armorSet) -> + view.fn 'currentArmor', (user) -> + user = { pub:{ items: {armor:0}, preferences: {gender:'m', armorSet:'v1'}}} unless user? + armor = user.pub.items.armor + {gender, armorSet} = user.pub.items if gender == 'f' str = "armor#{armor}_f" if parseInt(armor) > 1 diff --git a/src/app/index.coffee b/src/app/index.coffee index 9d5b9e5378..95c6a1448e 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -16,10 +16,10 @@ _ = require('underscore') setupListReferences = (model) -> taskTypes = ['habit', 'daily', 'todo', 'reward'] - _.each taskTypes, (type) -> model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids" + _.each taskTypes, (type) -> model.refList "_#{type}List", "_user.priv.tasks", "_user.priv.idLists.#{type}" setupModelFns = (model) -> - model.fn '_user._tnl', '_user.stats.lvl', (lvl) -> + model.fn '_tnl', '_user.pub.stats.lvl', (lvl) -> # see https://github.com/lefnire/habitrpg/issues/4 # also update in scoring.coffee. TODO create a function accessible in both locations (lvl*100)/5 @@ -47,12 +47,12 @@ get '/', (page, model, next) -> model.ref '_user', user batch = new schema.BatchUpdate(model) batch.startTransaction() + obj = batch.obj() # Setup Item Store - items = user.get('items') _view.items = - armor: content.items.armor[parseInt(items?.armor || 0) + 1] - weapon: content.items.weapon[parseInt(items?.weapon || 0) + 1] + armor: content.items.armor[parseInt(obj.pub.items?.armor || 0) + 1] + weapon: content.items.weapon[parseInt(obj.pub.items?.weapon || 0) + 1] potion: content.items.potion reroll: content.items.reroll @@ -65,8 +65,8 @@ get '/', (page, model, next) -> setupModelFns(model) # Subscribe to friends - if !_.isEmpty(user.get('party')) - model.subscribe model.query('users').party(user.get('party')), (err, party) -> + if !_.isEmpty(obj.pub.party) + model.subscribe model.query('users').party(obj.pub.party), (err, party) -> model.ref '_party', party page.render() @@ -83,8 +83,8 @@ ready (model) -> scoring.setModel(model) #set cron immediately - lastCron = user.get('lastCron') - user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new') + lastCron = user.get('priv.lastCron') + user.set('priv.lastCron', +new Date) if (!lastCron? or lastCron == 'new') # Setup model in scoring functions scoring.cron(resetDom) @@ -98,7 +98,7 @@ ready (model) -> require('../server/private').app(exports, model) - user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) -> + user.on 'set', 'priv.tasks.*.completed', (i, completed, previous, isLocal, passed) -> return if passed? && passed.cron # Don't do this stuff on cron direction = () -> return 'up' if completed==true and previous == false @@ -106,7 +106,7 @@ ready (model) -> throw new Error("Direction neither 'up' nor 'down' on checkbox set.") # Score the user based on todo task - task = user.at("tasks.#{i}") + task = user.at("priv.tasks.#{i}") scoring.score(i, direction()) exports.addTask = (e, el, next) -> @@ -145,10 +145,10 @@ ready (model) -> id = $(e.target).parents('li.task').attr('data-id') return unless id? - task = user.at "tasks.#{id}" + task = user.at "priv.tasks.#{id}" type = task.get('type') - history = task.get('history') + history = task.get('priv.history') if history and history.length>2 # prevent delete-and-recreate hack on red tasks if task.get('value') < 0 @@ -168,22 +168,22 @@ ready (model) -> # fix when query subscriptions implemented properly $('[rel=tooltip]').tooltip('hide') - ids = user.get("#{type}Ids") + ids = user.get("priv.idLists.#{type}") ids.splice(ids.indexOf(id),1) - user.del('tasks.'+id) - user.set("#{type}Ids", ids) + user.del('priv.tasks.'+id) + user.set("priv.idLists.#{type}", ids) exports.clearCompleted = (e, el) -> - todoIds = user.get('todoIds') + todoIds = user.get('priv.idLists.todo') removed = false _.each model.get('_todoList'), (task) -> if task.completed removed = true - user.del('tasks.'+task.id) + user.del('priv.tasks.'+task.id) todoIds.splice(todoIds.indexOf(task.id), 1) if removed - user.set('todoIds', todoIds) + user.set('priv.idLists.todo', todoIds) exports.toggleDay = (e, el) -> task = model.at(e.target) @@ -226,22 +226,22 @@ ready (model) -> #TODO: this should be working but it's not. so instead, i'm passing all needed values as data-attrs # item = model.at(e.target) - money = user.get 'stats.money' + gp = user.get 'pub.stats.gp' [type, value, index] = [ $(el).attr('data-type'), $(el).attr('data-value'), $(el).attr('data-index') ] - return if money < value - user.set 'stats.money', money - value + return if gp < value + user.set 'pub.stats.gp', gp - value if type == 'armor' - user.set 'items.armor', index + user.set 'pub.items.armor', index model.set '_view.items.armor', content.items.armor[parseInt(index) + 1] else if type == 'weapon' - user.set 'items.weapon', index + user.set 'pub.items.weapon', index model.set '_view.items.weapon', content.items.weapon[parseInt(index) + 1] else if type == 'potion' - hp = user.get 'stats.hp' + hp = user.get 'pub.stats.hp' hp += 15 hp = 50 if hp > 50 - user.set 'stats.hp', hp + user.set 'pub.stats.hp', hp exports.score = (e, el, next) -> direction = $(el).attr('data-direction') @@ -252,14 +252,14 @@ ready (model) -> revive = (batch) -> # Reset stats - batch.set 'stats.hp', 50 - batch.set 'stats.lvl', 1 - batch.set 'stats.money', 0 - batch.set 'stats.exp', 0 + batch.set 'pub.stats.hp', 50 + batch.set 'pub.stats.lvl', 1 + batch.set 'pub.stats.gp', 0 + batch.set 'pub.stats.exp', 0 # Reset items - batch.set 'items.armor', 0 - batch.set 'items.weapon', 0 + batch.set 'pub.items.armor', 0 + batch.set 'pub.items.weapon', 0 # Reset item store model.set '_view.items.armor', content.items.armor[1] @@ -275,33 +275,33 @@ ready (model) -> batch = new schema.BatchUpdate(model) batch.startTransaction() taskTypes = ['habit', 'daily', 'todo', 'reward'] - batch.set 'tasks', {} - _.each taskTypes, (type) -> batch.set "#{type}Ids", [] - batch.set 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens + batch.set 'priv.tasks', {} + _.each taskTypes, (type) -> batch.set "priv.idLists.#{type}", [] + batch.set 'priv.balance', 2 if user.get('priv.balance') < 2 #only if they haven't manually bought tokens revive(batch, true) batch.commit() resetDom(model) exports.closeKickstarterNofitication = (e, el) -> - user.set('notifications.kickstarter', 'hide') + user.set('priv.flags.kickstarter', 'hide') - exports.setMale = -> user.set('preferences.gender', 'm') - exports.setFemale = -> user.set('preferences.gender', 'f') - exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1') - exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2') + exports.setMale = -> user.set('pub.preferences.gender', 'm') + exports.setFemale = -> user.set('pub.preferences.gender', 'f') + exports.setArmorsetV1 = -> user.set('pub.preferences.armorSet', 'v1') + exports.setArmorsetV2 = -> user.set('pub.preferences.armorSet', 'v2') exports.addParty = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') debugger return if _.isEmpty(id) - if user.get('party').indexOf(id) != -1 + if user.get('pub.party').indexOf(id) != -1 model.set "_view.addPartyError", "#{id} already in party." return query = model.query('users').party([id]) model.fetch query, (err, users) -> partyMember = users.at(0).get() if partyMember?.id? - user.push('party', id) + user.push('pub.party', id) $('#add-party-modal').modal('hide') window.location.reload() #TODO break old subscription, setup new subscript, remove this reload model.set '_newPartyMember', '' @@ -310,6 +310,6 @@ ready (model) -> exports.emulateNextDay = -> yesterday = +moment().subtract('days', 1).toDate() - user.set 'lastCron', yesterday + user.set 'priv.lastCron', yesterday window.location.reload() diff --git a/src/app/schema.coffee b/src/app/schema.coffee index 71012b2f94..b9a6561694 100644 --- a/src/app/schema.coffee +++ b/src/app/schema.coffee @@ -30,11 +30,11 @@ userSchema = module.exports.newUserObject = -> # deep clone, else further new users get duplicate objects - newUser = require('lodash').cloneDeep userSchema + newUser = lodash.cloneDeep userSchema newUser.priv.apiToken = derby.uuid() for task in content.defaultTasks guid = task.id = derby.uuid() - newUser.tasks[guid] = task + newUser.priv.tasks[guid] = task switch task.type when 'habit' then newUser.priv.idLists.habit.push guid when 'daily' then newUser.priv.idLists.daily.push guid @@ -44,26 +44,24 @@ module.exports.newUserObject = -> module.exports.updateUser = (batch) -> user = batch.user - obj = user.batch.obj() + obj = batch.obj() batch.set('priv.apiToken', derby.uuid()) unless obj.priv.apiToken ## Task List Cleanup # FIXME temporary hack to fix lists (Need to figure out why these are happening) - tasks = user.get('tasks') + tasks = obj.priv.tasks _.each ['habit','daily','todo','reward'], (type) -> - path = "#{type}Ids" - # 1. remove duplicates # 2. restore missing zombie tasks back into list taskIds = _.pluck( _.where(tasks, {type:type}), 'id') - union = _.union user.get(path), taskIds + union = _.union obj.priv.idLists[type], taskIds # 2. remove empty (grey) tasks preened = _.filter(union, (val) -> _.contains(taskIds, val)) # There were indeed issues found, set the new list - batch.set(path, preened) # if _.difference(preened, userObj[path]).length != 0 + batch.set("priv.idLists.#{type}", preened) # if _.difference(preened, userObj[path]).length != 0 module.exports.BatchUpdate = BatchUpdate = (model) -> user = model.at("_user") @@ -102,9 +100,9 @@ module.exports.BatchUpdate = BatchUpdate = (model) -> eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok ### setStats: (stats) -> - stats ?= obj.stats + stats ?= obj.pub.stats that = @ - _.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key] + _.each Object.keys(stats), (key) -> that.set "pub.stats.#{key}", stats[key] # queue: (path, val) -> # # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790 diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 6a3de2725e..42c93e3140 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -20,8 +20,8 @@ setModel = (m) -> {modifiers} may manually pass in stats as {weapon, exp}. This is used for testing ### expModifier = (value, modifiers = {}) -> - weapon = modifiers.weapon || user.get('items.weapon') - lvl = modifiers.lvl || user.get('stats.lvl') + weapon = modifiers.weapon || user.get('pub.items.weapon') + lvl = modifiers.lvl || user.get('pub.stats.lvl') dmg = weapon * MODIFIER # each new weapon increases exp gain dmg += (lvl-1) * MODIFIER # same for lvls modified = value + (value * dmg) @@ -33,8 +33,8 @@ expModifier = (value, modifiers = {}) -> {modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing ### hpModifier = (value, modifiers = {}) -> - armor = modifiers.armor || user.get('items.armor') - lvl = modifiers.lvl || user.get('stats.lvl') + armor = modifiers.armor || user.get('pub.items.armor') + lvl = modifiers.lvl || user.get('pub.stats.lvl') ac = armor * MODIFIER # each new armor decreases HP loss ac += (lvl-1) * MODIFIER # same for lvls modified = value - (value * ac) @@ -62,37 +62,37 @@ updateStats = (newStats, batch) -> obj = batch.obj() # if user is dead, dont do anything - return if obj.stats.lvl == 0 + return if obj.pub.stats.lvl == 0 if newStats.hp? # Game Over if newStats.hp <= 0 - obj.stats.lvl = 0 # signifies dead - obj.stats.hp = 0 + obj.pub.stats.lvl = 0 # signifies dead + obj.pub.stats.hp = 0 return else - obj.stats.hp = newStats.hp + obj.pub.stats.hp = newStats.hp if newStats.exp? # level up & carry-over exp - tnl = user.get '_tnl' + tnl = model.get '_tnl' if newStats.exp >= tnl newStats.exp -= tnl - obj.stats.lvl++ - obj.stats.hp = 50 - if !obj.items.itemsEnabled and obj.stats.lvl >= 2 + obj.pub.stats.lvl++ + obj.pub.stats.hp = 50 + if !obj.priv.flags.itemsEnabled and obj.pub.stats.lvl >= 2 # Set to object, then also send to browser right away to get model.on() subscription notification - batch.set 'items.itemsEnabled', true - obj.items.itemsEnabled = true -# if !obj.flags.partyEnabled and obj.stats.lvl >= 3 -# batch.set 'flags.partyEnabled', true -# obj.flags.partyEnabled = true - obj.stats.exp = newStats.exp + batch.set 'priv.flags.itemsEnabled', true + obj.priv.flags.itemsEnabled = true + if !obj.priv.flags.partyEnabled and obj.pub.stats.lvl >= 3 + batch.set 'priv.flags.partyEnabled', true + obj.priv.flags.partyEnabled = true + obj.pub.stats.exp = newStats.exp - if newStats.money? - #FIXME what was I doing here? I can't remember, money isn't defined - money = 0.0 if (!money? or money<0) - obj.stats.money = newStats.money + if newStats.gp? + #FIXME what was I doing here? I can't remember, gp isn't defined + gp = 0.0 if (!gp? or gp<0) + obj.pub.stats.gp = newStats.gp # {taskId} task you want to score # {direction} 'up' or 'down' @@ -106,10 +106,10 @@ score = (taskId, direction, times, batch, cron) -> batch.startTransaction() obj = batch.obj() - {money, hp, exp, lvl} = obj.stats + {gp, hp, exp, lvl} = obj.pub.stats - taskPath = "tasks.#{taskId}" - taskObj = obj.tasks[taskId] + taskPath = "priv.tasks.#{taskId}" + taskObj = obj.priv.tasks[taskId] {type, value} = taskObj delta = 0 @@ -127,7 +127,7 @@ score = (taskId, direction, times, batch, cron) -> addPoints = -> modified = expModifier(delta) exp += modified - money += modified + gp += modified subtractPoints = -> modified = hpModifier(delta) @@ -162,21 +162,21 @@ score = (taskId, direction, times, batch, cron) -> # Don't adjust values for rewards calculateDelta(false) # purchase item - money -= taskObj.value + gp -= taskObj.value num = parseFloat(taskObj.value).toFixed(2) - # if too expensive, reduce health & zero money - if money < 0 - hp += money # hp - money difference - money = 0 + # if too expensive, reduce health & zero gp + if gp < 0 + hp += gp # hp - gp difference + gp = 0 taskObj.value = value batch.set "#{taskPath}.value", taskObj.value - origStats = _.clone obj.stats - updateStats {hp: hp, exp: exp, money: money}, batch + origStats = _.clone obj.pub.stats + updateStats {hp: hp, exp: exp, gp: gp}, batch if commit # newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*) - newStats = _.clone batch.obj().stats - _.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key] + newStats = _.clone batch.obj().pub.stats + _.each Object.keys(origStats), (key) -> obj.pub.stats[key] = origStats[key] batch.setStats(newStats) # batch.setStats() batch.commit() @@ -188,16 +188,16 @@ score = (taskId, direction, times, batch, cron) -> ### cron = (resetDom_cb) -> today = +new Date - daysPassed = helpers.daysBetween(today, user.get('lastCron')) + daysPassed = helpers.daysBetween(today, user.get('privlastCron')) if daysPassed > 0 batch = new schema.BatchUpdate(model) batch.startTransaction() - batch.set 'lastCron', today + batch.set 'priv.lastCron', today obj = batch.obj() - hpBefore = obj.stats.hp #we'll use this later so we can animate hp loss + hpBefore = obj.pub.stats.hp #we'll use this later so we can animate hp loss # Tally each task todoTally = 0 - _.each obj.tasks, (taskObj) -> + _.each obj.priv.tasks, (taskObj) -> unless taskObj.id? console.error "a task had a null id during cron, this should not be happening" return @@ -222,31 +222,31 @@ cron = (resetDom_cb) -> if type == 'daily' taskObj.history ?= [] taskObj.history.push { date: +new Date, value: value } - batch.set "tasks.#{taskObj.id}.history", taskObj.history - batch.set "tasks.#{taskObj.id}.completed", false + batch.set "priv.tasks.#{taskObj.id}.history", taskObj.history + batch.set "priv.tasks.#{taskObj.id}.completed", false else - value = obj.tasks[taskObj.id].value #get updated value + value = obj.priv.tasks[taskObj.id].value #get updated value absVal = if (completed) then Math.abs(value) else value todoTally += absVal # Finished tallying - obj.history ?= {}; obj.history.todos ?= []; obj.history.exp ?= [] - obj.history.todos.push { date: today, value: todoTally } + obj.priv.history ?= {}; obj.priv.history.todos ?= []; obj.priv.history.exp ?= [] + obj.priv.history.todos.push { date: today, value: todoTally } # tally experience - expTally = obj.stats.exp + expTally = obj.pub.stats.exp lvl = 0 #iterator - while lvl < (obj.stats.lvl-1) + while lvl < (obj.pub.stats.lvl-1) lvl++ expTally += (lvl*100)/5 - obj.history.exp.push { date: today, value: expTally } + obj.priv.history.exp.push { date: today, value: expTally } # Set the new user specs, and animate HP loss - [hpAfter, obj.stats.hp] = [obj.stats.hp, hpBefore] + [hpAfter, obj.pub.stats.hp] = [obj.pub.stats.hp, hpBefore] batch.setStats() - batch.set('history', obj.history) + batch.set('history', obj.priv.history) batch.commit() resetDom_cb(model) - setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss + setTimeout (-> user.set 'pub.stats.hp', hpAfter), 1000 # animate hp loss module.exports = { diff --git a/src/server/cron.coffee b/src/server/cron.coffee index d0f2ed5b24..029bc6d403 100644 --- a/src/server/cron.coffee +++ b/src/server/cron.coffee @@ -31,8 +31,8 @@ module.exports.deleteStaleAccounts = -> collection.findEach un_registered, (err, user) -> throw err if err return unless user? #why does this happen sometimes? - if !!user.lastCron # for now ignore missing crons, still looking into why this is happening - lastCron = new Date(user.lastCron) + if !!user.priv.lastCron # for now ignore missing crons, still looking into why this is happening + lastCron = new Date(user.priv.lastCron) diff = Math.abs(moment(today).sod().diff(moment(lastCron).sod(), "days")) if diff > 10 removeAccount(collection, user._id) diff --git a/src/server/private.coffee b/src/server/private.coffee index 807a1ad666..b9bb079cc8 100644 --- a/src/server/private.coffee +++ b/src/server/private.coffee @@ -35,9 +35,8 @@ module.exports.app = (appExports, model) -> appExports.buyReroll = (e, el, next) -> batch = new schema.BatchUpdate(model) obj = model.get('_user') - batch.set 'balance', obj.balance-1 - _.each obj.tasks, (task) -> batch.set("tasks.#{task.id}.value", 0) unless task.type == 'reward' - console.log(obj) + batch.set 'priv.balance', obj.priv.balance-1 + _.each obj.priv.tasks, (task) -> batch.set("priv.tasks.#{task.id}.value", 0) unless task.type == 'reward' batch.commit() module.exports.routes = (expressApp) -> @@ -54,8 +53,8 @@ module.exports.routes = (expressApp) -> userId = model.session.userId model.fetch "users.#{userId}", (err, user) -> model.ref '_user', "users.#{userId}" - model.set('_user.balance', model.get('_user.balance')+5) - model.set('_user.flags.ads','hide') + model.set('_user.priv.balance', model.get('_user.priv.balance')+5) + model.set('_user.priv.flags.ads','hide') return res.send(200) api_key = process.env.STRIPE_API_KEY # secret stripe API key diff --git a/src/server/serverRoutes.coffee b/src/server/serverRoutes.coffee index abd0ca8ee2..9665678e7a 100644 --- a/src/server/serverRoutes.coffee +++ b/src/server/serverRoutes.coffee @@ -49,8 +49,8 @@ module.exports = (expressApp, root, derby) -> # Create task if doesn't exist # TODO add service & icon to task - unless model.get("_user.tasks.#{taskId}") - model.refList "_habitList", "_user.tasks", "_user.habitIds" + unless model.get("_user.priv.tasks.#{taskId}") + model.refList "_habitList", "_user.priv.tasks", "_user.priv.idLists.habit" model.at('_habitList').push { id: taskId type: 'habit' diff --git a/views/app/index.html b/views/app/index.html index 66c9b504de..c59da66a1c 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -14,28 +14,28 @@
{_user.id}
API Token
-
{_user.preferences.api_token}
+
{_user.priv.apiToken}

Gender

- {#if equal(_user.preferences.gender,'f')} + {#if equal(_user.pub.preferences.gender,'f')}

Armor Set

{/} @@ -79,7 +79,7 @@ {/} -
+
@@ -100,7 +100,7 @@

Highly discouraged because red tasks provide good incentive to improve (read more). However, this becomes necessary after long bouts of bad habits.

<@footer> - {#if lessThan(_user.balance,1)} + {#if lessThan(_user.priv.balance,1)} Buy More TokensNot enough tokens {else} Re-Roll4 Tokens @@ -125,7 +125,7 @@ {/} - {#if equal(_user.notifications.kickstarter,'show')} + {#if equal(_user.priv.flags.kickstarter,'show')}
Dismiss Help Habit by backing the Kickstarter! Funds iPhone & Android apps, bug fixes, and the Groups feature. @@ -165,39 +165,39 @@
- {#if _user.flags.partyEnabled} + {#if _user.priv.flags.partyEnabled} {#each _party as :member} {/} @@ -281,7 +281,7 @@

Rewards

-
{gold(_user.stats.money)} {silver(_user.stats.money)}
+
{gold(_user.pub.stats.money)} {silver(_user.pub.stats.money)}
@@ -289,7 +289,7 @@ {#each _rewardList as :task}{/} - {#if _user.items.itemsEnabled} + {#if _user.priv.flags.itemsEnabled}
    {#with _view.items.armor as :item}{/} {#with _view.items.weapon as :item}{/} @@ -346,12 +346,12 @@
    - {tokens(_user.balance)} + {tokens(_user.priv.balance)} Tokens
    Reset
    - +
    From 9723f3c37a51943134f90f818efabdf91d0ed5ea Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 4 Feb 2013 22:35:07 -0500 Subject: [PATCH 05/31] don't need public/private paths after all, can check path requests in store accessControl callbacks --- .../20230204_user_public_private_paths.js | 68 ++++++++------- src/app/browser.coffee | 12 +-- src/app/helpers.coffee | 6 +- src/app/index.coffee | 82 +++++++++---------- src/app/schema.coffee | 66 +++++++-------- src/app/scoring.coffee | 76 ++++++++--------- src/server/cron.coffee | 4 +- src/server/private.coffee | 8 +- src/server/serverRoutes.coffee | 4 +- views/app/index.html | 48 +++++------ 10 files changed, 186 insertions(+), 188 deletions(-) diff --git a/migrations/20230204_user_public_private_paths.js b/migrations/20230204_user_public_private_paths.js index 66d080c19b..bb3b36ede5 100644 --- a/migrations/20230204_user_public_private_paths.js +++ b/migrations/20230204_user_public_private_paths.js @@ -52,42 +52,40 @@ db.users.find().forEach(function(user){ // Note 'public' and 'private' are reserved words var newUser = { auth: user.auth, // we need this top-level due to derby-auth - pub:{ - party: null, - invitations: [], - items: { - armor: user.items.armor || 0, - weapon: user.items.weapon || 0 - }, - stats: { - gp: user.stats.money || 0, - hp: user.stats.hp || 50, - exp: user.stats.exp || 0, - lvl: user.stats.lvl || 1 - }, - preferences: { - armorSet: user.preferences.armorSet || 'v1', - gender: user.preferences.gender || 'm' - } + apiToken: user.preferences.api_token || null, // set on update, we need derby.uuid() + preferences: { + armorSet: user.preferences.armorSet || 'v1', + gender: user.preferences.gender || 'm' }, - priv: { - balance: user.balance || 2, - lastCron: user.lastCron || +new Date, - tasks: user.tasks || {}, - history: user.history || [], - apiToken: user.preferences.api_token || null, // set on update, we need derby.uuid() - idLists: { - habit:user.habitList || [], - daily:user.dailyList || [], - todo:user.todoList || [], - reward:user.rewardList || [] - }, - flags: { - partyEnabled: false, - itemsEnabled: user.items.itemsEnabled || false, - kickstarter: user.notifications.kickstarter || 'show', - ads: user.flags.ads || null // null because it's set on registration - } + balance: user.balance || 2, + lastCron: user.lastCron || +new Date, + history: user.history || [], + stats: { + gp: user.stats.money || 0, + hp: user.stats.hp || 50, + exp: user.stats.exp || 0, + lvl: user.stats.lvl || 1 + }, + items: { + armor: user.items.armor || 0, + weapon: user.items.weapon || 0 + }, + tasks: user.tasks || {}, + idLists: { + habit: user.habitIds || [], + daily: user.dailyIds || [], + todo: user.todoIds || [], + reward: user.rewardIds || [] + }, + flags: { + partyEnabled: false, + itemsEnabled: user.items.itemsEnabled || false, + kickstarter: user.notifications.kickstarter || 'show', + ads: user.flags.ads || null // null because it's set on registration + }, + party: { + current: null, + invitation: null } }; diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 28ec5ac4d3..80e69e88ca 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -85,7 +85,7 @@ module.exports.setupGrowlNotifications = (model) -> statsNotification = (html, type) -> #don't show notifications if user dead - return if user.get('pub.stats.lvl') == 0 + return if user.get('stats.lvl') == 0 $.bootstrapGrowl html, type: type # (null, 'info', 'error', 'success') top_offset: 20 @@ -95,7 +95,7 @@ module.exports.setupGrowlNotifications = (model) -> allow_dismiss: true stackup_spacing: 10 # spacing between consecutive stacecked growls. - user.on 'set', 'priv.flags.itemsEnabled', (captures, args) -> + user.on 'set', 'flags.itemsEnabled', (captures, args) -> return unless captures == true message = "Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information." $('ul.items').popover @@ -109,7 +109,7 @@ module.exports.setupGrowlNotifications = (model) -> " $('ul.items').popover 'show' - user.on 'set', 'priv.flags.partyEnabled', (captures, args) -> + user.on 'set', 'flags.partyEnabled', (captures, args) -> return unless captures == true message = "Congratulations, you have unlocked the Party System! You can now group with your friends by adding their User Ids." $('#add-party-button').popover @@ -125,13 +125,13 @@ module.exports.setupGrowlNotifications = (model) -> # Setup listeners which trigger notifications - user.on 'set', 'pub.stats.hp', (captures, args) -> + user.on 'set', 'stats.hp', (captures, args) -> num = captures - args rounded = Math.abs(num.toFixed(1)) if num < 0 statsNotification "HP -#{rounded}", 'error' # lost hp from purchase - user.on 'set', 'pub.stats.gp', (captures, args) -> + user.on 'set', 'stats.gp', (captures, args) -> num = captures - args rounded = Math.abs(num.toFixed(1)) # made purchase @@ -143,6 +143,6 @@ module.exports.setupGrowlNotifications = (model) -> num = Math.abs(num) statsNotification "Exp,GP +#{rounded}", 'success' - user.on 'set', 'pub.stats.lvl', (captures, args) -> + user.on 'set', 'stats.lvl', (captures, args) -> if captures > args statsNotification(' Level Up!', 'info') \ No newline at end of file diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index 99c90198b4..20d2f446a9 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -57,9 +57,9 @@ module.exports.viewHelpers = (view) -> return gp/0.25 view.fn 'currentArmor', (user) -> - user = { pub:{ items: {armor:0}, preferences: {gender:'m', armorSet:'v1'}}} unless user? - armor = user.pub.items.armor - {gender, armorSet} = user.pub.items + user = { items: {armor:0}, preferences: {gender:'m', armorSet:'v1'}} unless user? + armor = user.items.armor + {gender, armorSet} = user.preferences if gender == 'f' str = "armor#{armor}_f" if parseInt(armor) > 1 diff --git a/src/app/index.coffee b/src/app/index.coffee index 95c6a1448e..dd5a25a80b 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -16,10 +16,10 @@ _ = require('underscore') setupListReferences = (model) -> taskTypes = ['habit', 'daily', 'todo', 'reward'] - _.each taskTypes, (type) -> model.refList "_#{type}List", "_user.priv.tasks", "_user.priv.idLists.#{type}" + _.each taskTypes, (type) -> model.refList "_#{type}List", "_user.tasks", "_user.idLists.#{type}" setupModelFns = (model) -> - model.fn '_tnl', '_user.pub.stats.lvl', (lvl) -> + model.fn '_tnl', '_user.stats.lvl', (lvl) -> # see https://github.com/lefnire/habitrpg/issues/4 # also update in scoring.coffee. TODO create a function accessible in both locations (lvl*100)/5 @@ -51,8 +51,8 @@ get '/', (page, model, next) -> # Setup Item Store _view.items = - armor: content.items.armor[parseInt(obj.pub.items?.armor || 0) + 1] - weapon: content.items.weapon[parseInt(obj.pub.items?.weapon || 0) + 1] + armor: content.items.armor[parseInt(obj.items?.armor || 0) + 1] + weapon: content.items.weapon[parseInt(obj.items?.weapon || 0) + 1] potion: content.items.potion reroll: content.items.reroll @@ -65,8 +65,8 @@ get '/', (page, model, next) -> setupModelFns(model) # Subscribe to friends - if !_.isEmpty(obj.pub.party) - model.subscribe model.query('users').party(obj.pub.party), (err, party) -> + if !_.isEmpty(obj.party) + model.subscribe model.query('users').party(obj.party), (err, party) -> model.ref '_party', party page.render() @@ -83,8 +83,8 @@ ready (model) -> scoring.setModel(model) #set cron immediately - lastCron = user.get('priv.lastCron') - user.set('priv.lastCron', +new Date) if (!lastCron? or lastCron == 'new') + lastCron = user.get('lastCron') + user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new') # Setup model in scoring functions scoring.cron(resetDom) @@ -98,7 +98,7 @@ ready (model) -> require('../server/private').app(exports, model) - user.on 'set', 'priv.tasks.*.completed', (i, completed, previous, isLocal, passed) -> + user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) -> return if passed? && passed.cron # Don't do this stuff on cron direction = () -> return 'up' if completed==true and previous == false @@ -106,7 +106,7 @@ ready (model) -> throw new Error("Direction neither 'up' nor 'down' on checkbox set.") # Score the user based on todo task - task = user.at("priv.tasks.#{i}") + task = user.at("tasks.#{i}") scoring.score(i, direction()) exports.addTask = (e, el, next) -> @@ -145,10 +145,10 @@ ready (model) -> id = $(e.target).parents('li.task').attr('data-id') return unless id? - task = user.at "priv.tasks.#{id}" + task = user.at "tasks.#{id}" type = task.get('type') - history = task.get('priv.history') + history = task.get('history') if history and history.length>2 # prevent delete-and-recreate hack on red tasks if task.get('value') < 0 @@ -168,22 +168,22 @@ ready (model) -> # fix when query subscriptions implemented properly $('[rel=tooltip]').tooltip('hide') - ids = user.get("priv.idLists.#{type}") + ids = user.get("idLists.#{type}") ids.splice(ids.indexOf(id),1) - user.del('priv.tasks.'+id) - user.set("priv.idLists.#{type}", ids) + user.del('tasks.'+id) + user.set("idLists.#{type}", ids) exports.clearCompleted = (e, el) -> - todoIds = user.get('priv.idLists.todo') + todoIds = user.get('idLists.todo') removed = false _.each model.get('_todoList'), (task) -> if task.completed removed = true - user.del('priv.tasks.'+task.id) + user.del('tasks.'+task.id) todoIds.splice(todoIds.indexOf(task.id), 1) if removed - user.set('priv.idLists.todo', todoIds) + user.set('idLists.todo', todoIds) exports.toggleDay = (e, el) -> task = model.at(e.target) @@ -226,22 +226,22 @@ ready (model) -> #TODO: this should be working but it's not. so instead, i'm passing all needed values as data-attrs # item = model.at(e.target) - gp = user.get 'pub.stats.gp' + gp = user.get 'stats.gp' [type, value, index] = [ $(el).attr('data-type'), $(el).attr('data-value'), $(el).attr('data-index') ] return if gp < value - user.set 'pub.stats.gp', gp - value + user.set 'stats.gp', gp - value if type == 'armor' - user.set 'pub.items.armor', index + user.set 'items.armor', index model.set '_view.items.armor', content.items.armor[parseInt(index) + 1] else if type == 'weapon' - user.set 'pub.items.weapon', index + user.set 'items.weapon', index model.set '_view.items.weapon', content.items.weapon[parseInt(index) + 1] else if type == 'potion' - hp = user.get 'pub.stats.hp' + hp = user.get 'stats.hp' hp += 15 hp = 50 if hp > 50 - user.set 'pub.stats.hp', hp + user.set 'stats.hp', hp exports.score = (e, el, next) -> direction = $(el).attr('data-direction') @@ -252,14 +252,14 @@ ready (model) -> revive = (batch) -> # Reset stats - batch.set 'pub.stats.hp', 50 - batch.set 'pub.stats.lvl', 1 - batch.set 'pub.stats.gp', 0 - batch.set 'pub.stats.exp', 0 + batch.set 'stats.hp', 50 + batch.set 'stats.lvl', 1 + batch.set 'stats.gp', 0 + batch.set 'stats.exp', 0 # Reset items - batch.set 'pub.items.armor', 0 - batch.set 'pub.items.weapon', 0 + batch.set 'items.armor', 0 + batch.set 'items.weapon', 0 # Reset item store model.set '_view.items.armor', content.items.armor[1] @@ -275,33 +275,33 @@ ready (model) -> batch = new schema.BatchUpdate(model) batch.startTransaction() taskTypes = ['habit', 'daily', 'todo', 'reward'] - batch.set 'priv.tasks', {} - _.each taskTypes, (type) -> batch.set "priv.idLists.#{type}", [] - batch.set 'priv.balance', 2 if user.get('priv.balance') < 2 #only if they haven't manually bought tokens + batch.set 'tasks', {} + _.each taskTypes, (type) -> batch.set "idLists.#{type}", [] + batch.set 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens revive(batch, true) batch.commit() resetDom(model) exports.closeKickstarterNofitication = (e, el) -> - user.set('priv.flags.kickstarter', 'hide') + user.set('flags.kickstarter', 'hide') - exports.setMale = -> user.set('pub.preferences.gender', 'm') - exports.setFemale = -> user.set('pub.preferences.gender', 'f') - exports.setArmorsetV1 = -> user.set('pub.preferences.armorSet', 'v1') - exports.setArmorsetV2 = -> user.set('pub.preferences.armorSet', 'v2') + exports.setMale = -> user.set('preferences.gender', 'm') + exports.setFemale = -> user.set('preferences.gender', 'f') + exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1') + exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2') exports.addParty = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') debugger return if _.isEmpty(id) - if user.get('pub.party').indexOf(id) != -1 + if user.get('party').indexOf(id) != -1 model.set "_view.addPartyError", "#{id} already in party." return query = model.query('users').party([id]) model.fetch query, (err, users) -> partyMember = users.at(0).get() if partyMember?.id? - user.push('pub.party', id) + user.push('party', id) $('#add-party-modal').modal('hide') window.location.reload() #TODO break old subscription, setup new subscript, remove this reload model.set '_newPartyMember', '' @@ -310,6 +310,6 @@ ready (model) -> exports.emulateNextDay = -> yesterday = +moment().subtract('days', 1).toDate() - user.set 'priv.lastCron', yesterday + user.set 'lastCron', yesterday window.location.reload() diff --git a/src/app/schema.coffee b/src/app/schema.coffee index b9a6561694..acfd0bbf44 100644 --- a/src/app/schema.coffee +++ b/src/app/schema.coffee @@ -6,62 +6,62 @@ derby = require 'derby' userSchema = # _id - pub: - stats: { gp: 0, exp: 0, lvl: 1, hp: 50 } - party: null - invitations: [] - items: { armor: 0, weapon: 0 } - preferences: { gender: 'm', armorSet: 'v1' } - priv: - idLists: - habit: [] - daily: [] - todo: [] - reward: [] - apiToken: null # set in newUserObject below - lastCron: 'new' #this will be replaced with `+new Date` on first run - balance: 2 - tasks: {} - flags: - partyEnabled: false - itemsEnabled: false - kickstarter: 'show' - # ads: 'show' # added on registration + stats: { gp: 0, exp: 0, lvl: 1, hp: 50 } + party: { + current: null + invitation: null + } + items: { armor: 0, weapon: 0 } + preferences: { gender: 'm', armorSet: 'v1' } + idLists: + habit: [] + daily: [] + todo: [] + reward: [] + apiToken: null # set in newUserObject below + lastCron: 'new' #this will be replaced with `+new Date` on first run + balance: 2 + tasks: {} + flags: + partyEnabled: false + itemsEnabled: false + kickstarter: 'show' + # ads: 'show' # added on registration module.exports.newUserObject = -> # deep clone, else further new users get duplicate objects newUser = lodash.cloneDeep userSchema - newUser.priv.apiToken = derby.uuid() + newUser.apiToken = derby.uuid() for task in content.defaultTasks guid = task.id = derby.uuid() - newUser.priv.tasks[guid] = task + newUser.tasks[guid] = task switch task.type - when 'habit' then newUser.priv.idLists.habit.push guid - when 'daily' then newUser.priv.idLists.daily.push guid - when 'todo' then newUser.priv.idLists.todo.push guid - when 'reward' then newUser.priv.idLists.reward.push guid + when 'habit' then newUser.idLists.habit.push guid + when 'daily' then newUser.idLists.daily.push guid + when 'todo' then newUser.idLists.todo.push guid + when 'reward' then newUser.idLists.reward.push guid return newUser module.exports.updateUser = (batch) -> user = batch.user obj = batch.obj() - batch.set('priv.apiToken', derby.uuid()) unless obj.priv.apiToken + batch.set('apiToken', derby.uuid()) unless obj.apiToken ## Task List Cleanup # FIXME temporary hack to fix lists (Need to figure out why these are happening) - tasks = obj.priv.tasks + tasks = obj.tasks _.each ['habit','daily','todo','reward'], (type) -> # 1. remove duplicates # 2. restore missing zombie tasks back into list taskIds = _.pluck( _.where(tasks, {type:type}), 'id') - union = _.union obj.priv.idLists[type], taskIds + union = _.union obj.idLists[type], taskIds # 2. remove empty (grey) tasks preened = _.filter(union, (val) -> _.contains(taskIds, val)) # There were indeed issues found, set the new list - batch.set("priv.idLists.#{type}", preened) # if _.difference(preened, userObj[path]).length != 0 + batch.set("idLists.#{type}", preened) # if _.difference(preened, userObj[path]).length != 0 module.exports.BatchUpdate = BatchUpdate = (model) -> user = model.at("_user") @@ -100,9 +100,9 @@ module.exports.BatchUpdate = BatchUpdate = (model) -> eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok ### setStats: (stats) -> - stats ?= obj.pub.stats + stats ?= obj.stats that = @ - _.each Object.keys(stats), (key) -> that.set "pub.stats.#{key}", stats[key] + _.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key] # queue: (path, val) -> # # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790 diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 42c93e3140..cbb4701577 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -20,8 +20,8 @@ setModel = (m) -> {modifiers} may manually pass in stats as {weapon, exp}. This is used for testing ### expModifier = (value, modifiers = {}) -> - weapon = modifiers.weapon || user.get('pub.items.weapon') - lvl = modifiers.lvl || user.get('pub.stats.lvl') + weapon = modifiers.weapon || user.get('items.weapon') + lvl = modifiers.lvl || user.get('stats.lvl') dmg = weapon * MODIFIER # each new weapon increases exp gain dmg += (lvl-1) * MODIFIER # same for lvls modified = value + (value * dmg) @@ -33,8 +33,8 @@ expModifier = (value, modifiers = {}) -> {modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing ### hpModifier = (value, modifiers = {}) -> - armor = modifiers.armor || user.get('pub.items.armor') - lvl = modifiers.lvl || user.get('pub.stats.lvl') + armor = modifiers.armor || user.get('items.armor') + lvl = modifiers.lvl || user.get('stats.lvl') ac = armor * MODIFIER # each new armor decreases HP loss ac += (lvl-1) * MODIFIER # same for lvls modified = value - (value * ac) @@ -62,37 +62,37 @@ updateStats = (newStats, batch) -> obj = batch.obj() # if user is dead, dont do anything - return if obj.pub.stats.lvl == 0 + return if obj.stats.lvl == 0 if newStats.hp? # Game Over if newStats.hp <= 0 - obj.pub.stats.lvl = 0 # signifies dead - obj.pub.stats.hp = 0 + obj.stats.lvl = 0 # signifies dead + obj.stats.hp = 0 return else - obj.pub.stats.hp = newStats.hp + obj.stats.hp = newStats.hp if newStats.exp? # level up & carry-over exp tnl = model.get '_tnl' if newStats.exp >= tnl newStats.exp -= tnl - obj.pub.stats.lvl++ - obj.pub.stats.hp = 50 - if !obj.priv.flags.itemsEnabled and obj.pub.stats.lvl >= 2 + obj.stats.lvl++ + obj.stats.hp = 50 + if !obj.flags.itemsEnabled and obj.stats.lvl >= 2 # Set to object, then also send to browser right away to get model.on() subscription notification - batch.set 'priv.flags.itemsEnabled', true - obj.priv.flags.itemsEnabled = true - if !obj.priv.flags.partyEnabled and obj.pub.stats.lvl >= 3 - batch.set 'priv.flags.partyEnabled', true - obj.priv.flags.partyEnabled = true - obj.pub.stats.exp = newStats.exp + batch.set 'flags.itemsEnabled', true + obj.flags.itemsEnabled = true + if !obj.flags.partyEnabled and obj.stats.lvl >= 3 + batch.set 'flags.partyEnabled', true + obj.flags.partyEnabled = true + obj.stats.exp = newStats.exp if newStats.gp? #FIXME what was I doing here? I can't remember, gp isn't defined gp = 0.0 if (!gp? or gp<0) - obj.pub.stats.gp = newStats.gp + obj.stats.gp = newStats.gp # {taskId} task you want to score # {direction} 'up' or 'down' @@ -106,10 +106,10 @@ score = (taskId, direction, times, batch, cron) -> batch.startTransaction() obj = batch.obj() - {gp, hp, exp, lvl} = obj.pub.stats + {gp, hp, exp, lvl} = obj.stats - taskPath = "priv.tasks.#{taskId}" - taskObj = obj.priv.tasks[taskId] + taskPath = "tasks.#{taskId}" + taskObj = obj.tasks[taskId] {type, value} = taskObj delta = 0 @@ -171,12 +171,12 @@ score = (taskId, direction, times, batch, cron) -> taskObj.value = value batch.set "#{taskPath}.value", taskObj.value - origStats = _.clone obj.pub.stats + origStats = _.clone obj.stats updateStats {hp: hp, exp: exp, gp: gp}, batch if commit # newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*) - newStats = _.clone batch.obj().pub.stats - _.each Object.keys(origStats), (key) -> obj.pub.stats[key] = origStats[key] + newStats = _.clone batch.obj().stats + _.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key] batch.setStats(newStats) # batch.setStats() batch.commit() @@ -192,12 +192,12 @@ cron = (resetDom_cb) -> if daysPassed > 0 batch = new schema.BatchUpdate(model) batch.startTransaction() - batch.set 'priv.lastCron', today + batch.set 'lastCron', today obj = batch.obj() - hpBefore = obj.pub.stats.hp #we'll use this later so we can animate hp loss + hpBefore = obj.stats.hp #we'll use this later so we can animate hp loss # Tally each task todoTally = 0 - _.each obj.priv.tasks, (taskObj) -> + _.each obj.tasks, (taskObj) -> unless taskObj.id? console.error "a task had a null id during cron, this should not be happening" return @@ -222,31 +222,31 @@ cron = (resetDom_cb) -> if type == 'daily' taskObj.history ?= [] taskObj.history.push { date: +new Date, value: value } - batch.set "priv.tasks.#{taskObj.id}.history", taskObj.history - batch.set "priv.tasks.#{taskObj.id}.completed", false + batch.set "tasks.#{taskObj.id}.history", taskObj.history + batch.set "tasks.#{taskObj.id}.completed", false else - value = obj.priv.tasks[taskObj.id].value #get updated value + value = obj.tasks[taskObj.id].value #get updated value absVal = if (completed) then Math.abs(value) else value todoTally += absVal # Finished tallying - obj.priv.history ?= {}; obj.priv.history.todos ?= []; obj.priv.history.exp ?= [] - obj.priv.history.todos.push { date: today, value: todoTally } + obj.history ?= {}; obj.history.todos ?= []; obj.history.exp ?= [] + obj.history.todos.push { date: today, value: todoTally } # tally experience - expTally = obj.pub.stats.exp + expTally = obj.stats.exp lvl = 0 #iterator - while lvl < (obj.pub.stats.lvl-1) + while lvl < (obj.stats.lvl-1) lvl++ expTally += (lvl*100)/5 - obj.priv.history.exp.push { date: today, value: expTally } + obj.history.exp.push { date: today, value: expTally } # Set the new user specs, and animate HP loss - [hpAfter, obj.pub.stats.hp] = [obj.pub.stats.hp, hpBefore] + [hpAfter, obj.stats.hp] = [obj.stats.hp, hpBefore] batch.setStats() - batch.set('history', obj.priv.history) + batch.set('history', obj.history) batch.commit() resetDom_cb(model) - setTimeout (-> user.set 'pub.stats.hp', hpAfter), 1000 # animate hp loss + setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss module.exports = { diff --git a/src/server/cron.coffee b/src/server/cron.coffee index 029bc6d403..d0f2ed5b24 100644 --- a/src/server/cron.coffee +++ b/src/server/cron.coffee @@ -31,8 +31,8 @@ module.exports.deleteStaleAccounts = -> collection.findEach un_registered, (err, user) -> throw err if err return unless user? #why does this happen sometimes? - if !!user.priv.lastCron # for now ignore missing crons, still looking into why this is happening - lastCron = new Date(user.priv.lastCron) + if !!user.lastCron # for now ignore missing crons, still looking into why this is happening + lastCron = new Date(user.lastCron) diff = Math.abs(moment(today).sod().diff(moment(lastCron).sod(), "days")) if diff > 10 removeAccount(collection, user._id) diff --git a/src/server/private.coffee b/src/server/private.coffee index b9bb079cc8..ed73b3b468 100644 --- a/src/server/private.coffee +++ b/src/server/private.coffee @@ -35,8 +35,8 @@ module.exports.app = (appExports, model) -> appExports.buyReroll = (e, el, next) -> batch = new schema.BatchUpdate(model) obj = model.get('_user') - batch.set 'priv.balance', obj.priv.balance-1 - _.each obj.priv.tasks, (task) -> batch.set("priv.tasks.#{task.id}.value", 0) unless task.type == 'reward' + batch.set 'balance', obj.balance-1 + _.each obj.tasks, (task) -> batch.set("tasks.#{task.id}.value", 0) unless task.type == 'reward' batch.commit() module.exports.routes = (expressApp) -> @@ -53,8 +53,8 @@ module.exports.routes = (expressApp) -> userId = model.session.userId model.fetch "users.#{userId}", (err, user) -> model.ref '_user', "users.#{userId}" - model.set('_user.priv.balance', model.get('_user.priv.balance')+5) - model.set('_user.priv.flags.ads','hide') + model.set('_user.balance', model.get('_user.balance')+5) + model.set('_user.flags.ads','hide') return res.send(200) api_key = process.env.STRIPE_API_KEY # secret stripe API key diff --git a/src/server/serverRoutes.coffee b/src/server/serverRoutes.coffee index 9665678e7a..0871499118 100644 --- a/src/server/serverRoutes.coffee +++ b/src/server/serverRoutes.coffee @@ -49,8 +49,8 @@ module.exports = (expressApp, root, derby) -> # Create task if doesn't exist # TODO add service & icon to task - unless model.get("_user.priv.tasks.#{taskId}") - model.refList "_habitList", "_user.priv.tasks", "_user.priv.idLists.habit" + unless model.get("_user.tasks.#{taskId}") + model.refList "_habitList", "_user.tasks", "_user.idLists.habit" model.at('_habitList').push { id: taskId type: 'habit' diff --git a/views/app/index.html b/views/app/index.html index c59da66a1c..e6539cf291 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -14,28 +14,28 @@
    {_user.id}
    API Token
    -
    {_user.priv.apiToken}
    +
    {_user.apiToken}

    Gender

    - {#if equal(_user.pub.preferences.gender,'f')} + {#if equal(_user.preferences.gender,'f')}

    Armor Set

    {/} @@ -79,7 +79,7 @@ {/} -
    +
- - + +
-
Lvl {_user.stats.lvl}
+
Lvl {_user.pub.stats.lvl}
-
- {round(_user.stats.hp)} / 50 +
+ {round(_user.pub.stats.hp)} / 50
-
+
{#if _user.history.exp}   {/} - {round(_user.stats.exp)} / {_user._tnl} + {round(_user.pub.stats.exp)} / {_tnl}
- - + +
-
Lvl {:member.stats.lvl}
+
Lvl {:member.pub.stats.lvl}
@@ -100,7 +100,7 @@

Highly discouraged because red tasks provide good incentive to improve (read more). However, this becomes necessary after long bouts of bad habits.

<@footer> - {#if lessThan(_user.priv.balance,1)} + {#if lessThan(_user.balance,1)} Buy More TokensNot enough tokens {else} Re-Roll4 Tokens @@ -125,7 +125,7 @@ {/} - {#if equal(_user.priv.flags.kickstarter,'show')} + {#if equal(_user.flags.kickstarter,'show')}
Dismiss Help Habit by backing the Kickstarter! Funds iPhone & Android apps, bug fixes, and the Groups feature. @@ -165,39 +165,39 @@
- {#if _user.priv.flags.partyEnabled} + {#if _user.flags.partyEnabled} {#each _party as :member} {/} @@ -281,7 +281,7 @@

Rewards

-
{gold(_user.pub.stats.money)} {silver(_user.pub.stats.money)}
+
{gold(_user.stats.money)} {silver(_user.stats.money)}
@@ -289,7 +289,7 @@ {#each _rewardList as :task}{/} - {#if _user.priv.flags.itemsEnabled} + {#if _user.flags.itemsEnabled}
    {#with _view.items.armor as :item}{/} {#with _view.items.weapon as :item}{/} @@ -346,12 +346,12 @@
    - {tokens(_user.priv.balance)} + {tokens(_user.balance)} Tokens
    Reset
    - + From a67c82aab50dcfeea234d5958f32773e597dbfdf Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 00:20:58 -0500 Subject: [PATCH 06/31] accessControl basic framework for party system --- src/app/index.coffee | 32 ++++++------------------ src/app/party.coffee | 55 +++++++++++++++++++++++++++++++++++++++++ src/server/index.coffee | 3 ++- src/server/store.coffee | 50 +++++++++++++++++++++++++++++++++++-- views/app/index.html | 54 ++++++++++++++++++++++++++++++++++------ 5 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 src/app/party.coffee diff --git a/src/app/index.coffee b/src/app/index.coffee index dd5a25a80b..6276f8ff01 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -10,8 +10,8 @@ content = require './content' scoring = require './scoring' schema = require './schema' helpers = require './helpers' -helpers.viewHelpers view browser = require './browser' +helpers.viewHelpers view _ = require('underscore') setupListReferences = (model) -> @@ -65,9 +65,9 @@ get '/', (page, model, next) -> setupModelFns(model) # Subscribe to friends - if !_.isEmpty(obj.party) - model.subscribe model.query('users').party(obj.party), (err, party) -> - model.ref '_party', party +# if obj.party?.current? +# model.subscribe model.query('users').party(obj.party), (err, party) -> +# model.ref '_party', party page.render() @@ -96,6 +96,8 @@ ready (model) -> browser.setupTour(model) browser.setupGrowlNotifications(model) unless model.get('_view.mobileDevice') + require('./party')(exports, model) + require('../server/private').app(exports, model) user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) -> @@ -282,34 +284,16 @@ ready (model) -> batch.commit() resetDom(model) - exports.closeKickstarterNofitication = (e, el) -> - user.set('flags.kickstarter', 'hide') + exports.closeKickstarterNofitication = (e, el) -> user.set('flags.kickstarter', 'hide') exports.setMale = -> user.set('preferences.gender', 'm') exports.setFemale = -> user.set('preferences.gender', 'f') exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1') exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2') - exports.addParty = -> - id = model.get('_newPartyMember').replace(/[\s"]/g, '') - debugger - return if _.isEmpty(id) - if user.get('party').indexOf(id) != -1 - model.set "_view.addPartyError", "#{id} already in party." - return - query = model.query('users').party([id]) - model.fetch query, (err, users) -> - partyMember = users.at(0).get() - if partyMember?.id? - user.push('party', id) - $('#add-party-modal').modal('hide') - window.location.reload() #TODO break old subscription, setup new subscript, remove this reload - model.set '_newPartyMember', '' - else - model.set "_view.addPartyError", "User with id #{id} not found." - exports.emulateNextDay = -> yesterday = +moment().subtract('days', 1).toDate() user.set 'lastCron', yesterday window.location.reload() + diff --git a/src/app/party.coffee b/src/app/party.coffee new file mode 100644 index 0000000000..7beba07e1b --- /dev/null +++ b/src/app/party.coffee @@ -0,0 +1,55 @@ +_ = require('underscore') + +module.exports = (appExports, model) -> + user = model.at('_user') + + appExports.partyCreate = -> + newParty = model.get("_newParty") + id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [], invites: [] } + user.set 'party', {current: id, invitation: null, leader: true} + + appExports.partyInvite = -> + id = model.get('_newPartyMember').replace(/[\s"]/g, '') + return if _.isEmpty(id) + + obj = user.get() + query = model.query('users').party([id]) + model.fetch query, (err, users) -> + partyMember = users.at(0).get() + if !partyMember?.id? + model.set "_view.partyError", "User with id #{id} not found." + return + else if partyMember.party.current?.id? or partyMember.party.invitation? + model.set "_view.partyError", "User already in a party or pending invitation." + return + else + model.push "parties.#{obj.party.current}.invites", id + model.set "users.#{id}.party.invitation", obj.party.current + $.bootstrapGrowl "Invitation Sent." + $('#party-modal').modal('hide') + #model.set '_newPartyMember', '' + #window.location.reload() #TODO break old subscription, setup new subscript, remove this reload + + appExports.partyAccept = -> + invitation = user.get('party.invitation') + user.set 'party.current', invitation + user.set 'party.invitation', null + model.push "parties.#{invitation}.members", user.get('id') + window.location.reload() + + appExports.partyReject = -> + user.set 'party.invitation', null + # TODO notify sender + + appExports.partyLeave = -> + id = user.get('party.current') + user.set 'party.current', null + members = model.get ("parties.#{id}.members") + index = members.indexOf(user.get('id')) + model.set "parties.#{id}.members", members.slice(index) + window.location.reload() + + appExports.partyDisband = -> + + + user.on 'set', 'parties.invitation', (after, before) -> diff --git a/src/server/index.coffee b/src/server/index.coffee index e7a4e5de14..1afc7864b6 100644 --- a/src/server/index.coffee +++ b/src/server/index.coffee @@ -8,6 +8,7 @@ serverError = require './serverError' MongoStore = require('connect-mongo')(express) auth = require 'derby-auth' priv = require './private' +habitrpgStore = require('./store') ## Run server cron ## require('./cron').deleteStaleAccounts() @@ -31,7 +32,6 @@ derby.use(require 'racer-db-mongo') store = derby.createStore db: {type: 'Mongo', uri: process.env.NODE_DB_URI, safe:true} listen: server -require('./store')(store) #setup custom accessControl ONE_YEAR = 1000 * 60 * 60 * 24 * 365 root = path.dirname path.dirname __dirname @@ -48,6 +48,7 @@ options = domain: process.env.BASE_URL || 'http://localhost:3000' allowPurl: true schema: require('../app/schema').newUserObject() + customAccessControl: habitrpgStore.customAccessControl mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, -> expressApp diff --git a/src/server/store.coffee b/src/server/store.coffee index d071a2a37e..e36aaa3442 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -2,7 +2,41 @@ Setup read / write access @param store ### -module.exports = (store) -> + +module.exports.customAccessControl = (store) -> + +# store.readPathAccess "users.*", () -> # captures, next +# next = arguments[arguments.length-1] +# return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 +# return next(true) + + store.readPathAccess "users.*", -> # captures, next) -> + return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 + console.log arguments + captures = arguments[0] + next = arguments[arguments.length - 1] + sameSession = captures is @session.userId + isServer = false #!this.req.socket; //TODO how to determine if request came from server, as in REST? + next sameSession or isServer + + store.writeAccess "*", "users.*", -> # captures, value, next) -> + return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 + [captures, next] = [arguments[0].split('.'), arguments[arguments.length-1]] + uid = captures.shift() + attrPath = captures.join('.') # new array shifted left, after shift() was run + + # TODO the server can write to anything - aka, REST + #return next(true) if !this.req.socket; + + # public access to users.*.party.invitation (TODO, lock down a bit more) + console.log attrPath + return next(true) if (attrPath == 'party.invitation') + + # Same session (user.id = this.session.userId) + return next(true) if uid is @session.userId + + next(false) + # store.writeAccess "*", "users.*.balance", (id, newBalance, next) -> # return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 @@ -34,7 +68,19 @@ module.exports = (store) -> ### store.query.expose "users", "party", (ids) -> @where("id").within(ids) - .only('stats', 'preferences.gender', 'preferences.armorSet', 'items', 'auth.local.username', 'auth.facebook.displayName') + .only('stats', + 'items', + 'party', + 'preferences.gender', + 'preferences.armorSet', + 'auth.local.username', + 'auth.facebook.displayName') store.queryAccess "users", "party", (ids, next) -> next(true) # no harm in public user stats + + store.readPathAccess "parties.*", -> + arguments[arguments.length-1](true) + + store.writeAccess "*", "parties.*", -> + arguments[arguments.length-1](true) diff --git a/views/app/index.html b/views/app/index.html index e6539cf291..53a9113506 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -40,6 +40,9 @@ {/} +
    + Reset + <@footer> @@ -108,14 +111,47 @@ - - - {#if _view.addPartyError} -
    {_view.addPartyError}
    + +

    + {#if _user.party.current}Invite Member To "{_user.party.current}" + {else if _user.party.invitation}You're Invited To A Party + {else}Create A Party{/} +

    + + {#if _user.party.current} + + {#each _party as :member} + {:member.id} + {/} + + {#if _view.partyError} +
    {_view.partyError}
    {/} - + + Leave + + {else} + + + {#if _user.party.invitation} + + Accept + Reject + {else} + +
    + {#if _view.partyError} +
    {_view.partyError}
    + {/} + + + + {/} + {/} + +
    @@ -142,13 +178,16 @@ Login / Register {else}
    - +
    @@ -200,7 +239,6 @@
    Lvl {:member.stats.lvl}
    {/} -
{/} From 6245d0adce15cccf83e2129e1155b7ea86faea93 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 01:37:13 -0500 Subject: [PATCH 07/31] setup party model subscriptions (currently very inefficient, and unsubscribe not being utilized) --- src/app/index.coffee | 12 +++--------- src/app/party.coffee | 31 ++++++++++++++++++++++--------- src/server/store.coffee | 5 +++++ views/app/index.html | 12 ++++++------ 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 6276f8ff01..9f77cbcb8e 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -11,6 +11,7 @@ scoring = require './scoring' schema = require './schema' helpers = require './helpers' browser = require './browser' +party = require './party' helpers.viewHelpers view _ = require('underscore') @@ -24,10 +25,6 @@ setupModelFns = (model) -> # also update in scoring.coffee. TODO create a function accessible in both locations (lvl*100)/5 -# model.fn '_party', '_user.party', (ids) -> -# model.fetch model.query('users').party(ids), (err, party) -> -# model.set '_view.party', party - # ========== ROUTES ========== get '/', (page, model, next) -> @@ -64,10 +61,7 @@ get '/', (page, model, next) -> setupListReferences(model) setupModelFns(model) - # Subscribe to friends -# if obj.party?.current? -# model.subscribe model.query('users').party(obj.party), (err, party) -> -# model.ref '_party', party + party.server(model) page.render() @@ -96,7 +90,7 @@ ready (model) -> browser.setupTour(model) browser.setupGrowlNotifications(model) unless model.get('_view.mobileDevice') - require('./party')(exports, model) + party.app(exports, model) require('../server/private').app(exports, model) diff --git a/src/app/party.coffee b/src/app/party.coffee index 7beba07e1b..9ea816407a 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,14 +1,23 @@ _ = require('underscore') -module.exports = (appExports, model) -> +module.exports.server = (model) -> + obj = model.get('_user') + if obj.party?.current + model.subscribe model.query('parties').withId(obj.party.current), (err, parties) -> + party = parties.at(0) + model.ref '_party', party + model.subscribe model.query('users').party(party.get('members')), (err, members) -> + model.ref '_partyMembers', members + +module.exports.app = (exports, model) -> user = model.at('_user') - appExports.partyCreate = -> + exports.partyCreate = -> newParty = model.get("_newParty") id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [], invites: [] } user.set 'party', {current: id, invitation: null, leader: true} - appExports.partyInvite = -> + exports.partyInvite = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') return if _.isEmpty(id) @@ -30,18 +39,18 @@ module.exports = (appExports, model) -> #model.set '_newPartyMember', '' #window.location.reload() #TODO break old subscription, setup new subscript, remove this reload - appExports.partyAccept = -> + exports.partyAccept = -> invitation = user.get('party.invitation') user.set 'party.current', invitation user.set 'party.invitation', null model.push "parties.#{invitation}.members", user.get('id') window.location.reload() - appExports.partyReject = -> + exports.partyReject = -> user.set 'party.invitation', null # TODO notify sender - appExports.partyLeave = -> + exports.partyLeave = -> id = user.get('party.current') user.set 'party.current', null members = model.get ("parties.#{id}.members") @@ -49,7 +58,11 @@ module.exports = (appExports, model) -> model.set "parties.#{id}.members", members.slice(index) window.location.reload() - appExports.partyDisband = -> + exports.partyDisband = -> - - user.on 'set', 'parties.invitation', (after, before) -> +# user.on 'set', 'parties.invitation', (after, before) -> +# +# model.on '*', '_party.members', (ids) -> +# # TODO unsubscribe to previous subscription +# model.subscribe model.query('users').party(ids), (err, members) -> +# model.ref '_view.partyMembers', members diff --git a/src/server/store.coffee b/src/server/store.coffee index e36aaa3442..07e2357af1 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -79,6 +79,11 @@ module.exports.customAccessControl = (store) -> store.queryAccess "users", "party", (ids, next) -> next(true) # no harm in public user stats + store.query.expose "parties", "withId", (id) -> + @where("id").equals(id) + store.queryAccess "parties", "withId", (id, next) -> + next(true) + store.readPathAccess "parties.*", -> arguments[arguments.length-1](true) diff --git a/views/app/index.html b/views/app/index.html index 53a9113506..4ebdb3fa9d 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -113,14 +113,14 @@

- {#if _user.party.current}Invite Member To "{_user.party.current}" - {else if _user.party.invitation}You're Invited To A Party + {#if _user.party.current}Invite Member To "{_party.name}" + {else if _user.party.invitation}You're Invited To "{_party.name}" {else}Create A Party{/}

{#if _user.party.current} - {#each _party as :member} + {#each _partyMembers as :member} {:member.id} {/}
@@ -229,8 +229,8 @@ - {#if _user.flags.partyEnabled} - {#each _party as :member} + {#if _party} + {#each _partyMembers as :member}
+ {#unless equal(:member.id, _userId)} + + {/} {/} {/} From fe544de798aa82e956d8149ea8fb08ec30ca0e69 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 11:44:51 -0500 Subject: [PATCH 09/31] party subscriptions properly working --- src/app/index.coffee | 46 ++++++++++++++++++++++---------------------- src/app/party.coffee | 22 +++++++++++++-------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 9f77cbcb8e..90b228e740 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -36,34 +36,34 @@ get '/', (page, model, next) -> #if req.headers['x-forwarded-proto']!='https' and process.env.NODE_ENV=='production' # return page.redirect 'https://' + req.headers.host + req.url - #FIXME subscribing to this query causes "Fatal Error: Unauuthorized" after conncetion for a time (racer/lib/accessControl/accessControl.Store.js) - #q = model.query('users').withId(model.session.userId) - q = "users.#{model.session.userId}" - model.subscribe q, (err, user) -> - #user = result.at(0) - model.ref '_user', user - batch = new schema.BatchUpdate(model) - batch.startTransaction() - obj = batch.obj() + party.server model, (queries) -> + subscribeCb = -> + [err, party, members, user] = [arguments[0], arguments[1].at(0), arguments[2], arguments[3].at(0)] + console.log {err:err, user:user.get(), party:party.get(), members:members.get()} + model.ref '_user', user + model.ref '_party', party + model.ref '_partyMembers', members + batch = new schema.BatchUpdate(model) + batch.startTransaction() + obj = batch.obj() - # Setup Item Store - _view.items = - armor: content.items.armor[parseInt(obj.items?.armor || 0) + 1] - weapon: content.items.weapon[parseInt(obj.items?.weapon || 0) + 1] - potion: content.items.potion - reroll: content.items.reroll + # Setup Item Store + _view.items = + armor: content.items.armor[parseInt(obj.items?.armor || 0) + 1] + weapon: content.items.weapon[parseInt(obj.items?.weapon || 0) + 1] + potion: content.items.potion + reroll: content.items.reroll - model.set '_view', _view + model.set '_view', _view - schema.updateUser(batch) - batch.commit() + schema.updateUser(batch) + batch.commit() - setupListReferences(model) - setupModelFns(model) + setupListReferences(model) + setupModelFns(model) - party.server(model) - - page.render() + page.render() + model.subscribe.apply model, queries.concat(subscribeCb) # ========== CONTROLLER FUNCTIONS ========== diff --git a/src/app/party.coffee b/src/app/party.coffee index f40df7fce9..83fcd6592b 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,13 +1,19 @@ _ = require('underscore') -module.exports.server = (model) -> - obj = model.get('_user') - if obj.party?.current - model.subscribe model.query('parties').withId(obj.party.current), (err, parties) -> - party = parties.at(0) - model.ref '_party', party - model.subscribe model.query('users').party(party.get('members')), (err, members) -> - model.ref '_partyMembers', members +module.exports.server = (model, cb) -> + + selfQ = model.query('users').withId(model.session.userId) + model.fetch selfQ, (err, self) -> + console.error err if err + currentParty = self.at(0).get('party.current') + if currentParty + partiesQ = model.query('parties').withId(currentParty) + model.fetch partiesQ, (err, parties) -> + console.error err if err + membersQ = model.query('users').party(parties.at(0).get('members')) + cb([partiesQ, membersQ, selfQ]) + else + cb([selfQ]) module.exports.app = (exports, model) -> user = model.at('_user') From 966d81b9b80829b0b175fd15423c137c613791d3 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 16:11:28 -0500 Subject: [PATCH 10/31] some @WIP hacks on model subscriptions --- src/app/index.coffee | 33 ++++++++++++++++++++++++++----- src/app/party.coffee | 47 +++++++++++++++++--------------------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 90b228e740..c5839f69f8 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -36,13 +36,36 @@ get '/', (page, model, next) -> #if req.headers['x-forwarded-proto']!='https' and process.env.NODE_ENV=='production' # return page.redirect 'https://' + req.headers.host + req.url - party.server model, (queries) -> + # This used to be in party.server(model, cb), but was getting `TypeError: Object # has no method 'server'` + # on the second load for some reason + partyQueries = (cb) -> + selfQ = model.query('users').withId(model.session.userId) + model.fetch selfQ, (err, self) -> + console.error err if err + currentParty = self.at(0).get('party.current') + console.log {self: self.get()} + console.log {currentParty:currentParty} + if currentParty + partiesQ = model.query('parties').withId(currentParty) + model.fetch partiesQ, (err, parties) -> + console.error err if err + membersQ = model.query('users').party(parties.at(0).get('members')) + cb([partiesQ, membersQ, selfQ]) + else + cb([selfQ]) + + partyQueries (queries) -> subscribeCb = -> - [err, party, members, user] = [arguments[0], arguments[1].at(0), arguments[2], arguments[3].at(0)] - console.log {err:err, user:user.get(), party:party.get(), members:members.get()} + [err, user] = [arguments[0], null] + throw err if err + if arguments.length == 4 + [party, members, user] = [arguments[1].at(0), arguments[2], arguments[3].at(0)] + console.log {err:err, user:user.get(), party:party.get(), members:members.get()} + model.ref '_party', party + model.ref '_partyMembers', members + else + user = arguments[1].at(0) model.ref '_user', user - model.ref '_party', party - model.ref '_partyMembers', members batch = new schema.BatchUpdate(model) batch.startTransaction() obj = batch.obj() diff --git a/src/app/party.coffee b/src/app/party.coffee index 83fcd6592b..2da7929f54 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,19 +1,5 @@ _ = require('underscore') - -module.exports.server = (model, cb) -> - - selfQ = model.query('users').withId(model.session.userId) - model.fetch selfQ, (err, self) -> - console.error err if err - currentParty = self.at(0).get('party.current') - if currentParty - partiesQ = model.query('parties').withId(currentParty) - model.fetch partiesQ, (err, parties) -> - console.error err if err - membersQ = model.query('users').party(parties.at(0).get('members')) - cb([partiesQ, membersQ, selfQ]) - else - cb([selfQ]) +schema = require './schema' module.exports.app = (exports, model) -> user = model.at('_user') @@ -30,6 +16,7 @@ module.exports.app = (exports, model) -> obj = user.get() query = model.query('users').party([id]) model.fetch query, (err, users) -> + throw err if err partyMember = users.at(0).get() if !partyMember?.id? model.set "_view.partyError", "User with id #{id} not found." @@ -47,31 +34,33 @@ module.exports.app = (exports, model) -> exports.partyAccept = -> invitation = user.get('party.invitation') - user.set 'party.current', invitation - user.set 'party.invitation', null model.push "parties.#{invitation}.members", user.get('id') - window.location.reload() + user.set 'party.invitation', null, -> + user.set 'party.current', invitation, -> + window.location.reload() exports.partyReject = -> user.set 'party.invitation', null + # TODO splice parties.*.invites[key] # TODO notify sender exports.partyLeave = -> id = user.get('party.current') - user.set 'party.current', null - members = model.get ("parties.#{id}.members") - index = members.indexOf(user.get('id')) - newMembers = members.slice(index) - if (newMembers.length == 0) - # last member out, kill the party - model.del "parties.#{id}" - else - model.set "parties.#{id}.members", members.slice(index) - window.location.reload() + user.set 'party.current', null, -> + members = model.get "parties.#{id}.members" + index = members.indexOf(user.get('id')) + newMembers = members.slice(index) + model.set "parties.#{id}.members", newMembers -> + if (newMembers.length == 0) + # last member out, kill the party + model.del "parties.#{id}", -> window.location.reload() + else + window.location.reload() exports.partyDisband = -> -# user.on 'set', 'parties.invitation', (after, before) -> + user.on 'set', 'party.invitation', (id) -> + model.fetch "parties.#{id}", (err, party) -> model.set '_party', party # # model.on '*', '_party.members', (ids) -> # # TODO unsubscribe to previous subscription From 98f1e0e6900b4cbdd52f0cb8cae33024aef107ce Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 18:12:39 -0500 Subject: [PATCH 11/31] bunch of model subscription fixes, mostly due to @lackac's racer session fix --- src/app/index.coffee | 89 ++++++++++++++++++++--------------------- src/app/party.coffee | 73 ++++++++++++++++++++------------- src/server/store.coffee | 13 +++--- 3 files changed, 95 insertions(+), 80 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index c5839f69f8..33b63cd5a8 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -38,55 +38,54 @@ get '/', (page, model, next) -> # This used to be in party.server(model, cb), but was getting `TypeError: Object # has no method 'server'` # on the second load for some reason - partyQueries = (cb) -> - selfQ = model.query('users').withId(model.session.userId) - model.fetch selfQ, (err, self) -> - console.error err if err - currentParty = self.at(0).get('party.current') - console.log {self: self.get()} - console.log {currentParty:currentParty} - if currentParty - partiesQ = model.query('parties').withId(currentParty) - model.fetch partiesQ, (err, parties) -> - console.error err if err - membersQ = model.query('users').party(parties.at(0).get('members')) - cb([partiesQ, membersQ, selfQ]) - else - cb([selfQ]) + selfQ = model.query('users').withId(model.session.userId) + model.subscribe selfQ, (err, users) -> + console.log err if err - partyQueries (queries) -> - subscribeCb = -> - [err, user] = [arguments[0], null] - throw err if err - if arguments.length == 4 - [party, members, user] = [arguments[1].at(0), arguments[2], arguments[3].at(0)] - console.log {err:err, user:user.get(), party:party.get(), members:members.get()} + user = users.at(0) + model.ref '_user', user + obj = user.get() + + batch = new schema.BatchUpdate(model) + batch.startTransaction() + obj = batch.obj() + + # Setup Item Store + _view.items = + armor: content.items.armor[parseInt(obj.items?.armor || 0) + 1] + weapon: content.items.weapon[parseInt(obj.items?.weapon || 0) + 1] + potion: content.items.potion + reroll: content.items.reroll + + model.set '_view', _view + + schema.updateUser(batch) + batch.commit() + + setupListReferences(model) + setupModelFns(model) + + if obj.party?.current? + console.log obj.party.current + console.log err if err + partiesQ = model.query('parties').withId(obj.party.current) + model.subscribe partiesQ, (err, parties) -> + console.log err if err + party = parties.at(0) model.ref '_party', party - model.ref '_partyMembers', members - else - user = arguments[1].at(0) - model.ref '_user', user - batch = new schema.BatchUpdate(model) - batch.startTransaction() - obj = batch.obj() - - # Setup Item Store - _view.items = - armor: content.items.armor[parseInt(obj.items?.armor || 0) + 1] - weapon: content.items.weapon[parseInt(obj.items?.weapon || 0) + 1] - potion: content.items.potion - reroll: content.items.reroll - - model.set '_view', _view - - schema.updateUser(batch) - batch.commit() - - setupListReferences(model) - setupModelFns(model) + membersQ = model.query('users').party(parties.at(0).get('members')) + model.subscribe membersQ, (err, members) -> + throw err if err + model.ref '_partyMembers', members + # Here's a hack we need to get fixed (hopefully Lever will) - later model.queries override previous model.queries' + # returned fields. Aka, we need this here otherwise we only get the "public" fields for the current user, which + # are defined in model.query('users')party() + model.subscribe selfQ, (err, users) -> + model.ref '_user', users.at(0) + page.render() + else page.render() - model.subscribe.apply model, queries.concat(subscribeCb) # ========== CONTROLLER FUNCTIONS ========== diff --git a/src/app/party.coffee b/src/app/party.coffee index 2da7929f54..7d212f46d9 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,15 +1,18 @@ _ = require('underscore') schema = require './schema' -module.exports.app = (exports, model) -> +module.exports.app = (appExports, model) -> user = model.at('_user') - exports.partyCreate = -> + appExports.partyCreate = -> newParty = model.get("_newParty") - id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')] } + id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } user.set 'party', {current: id, invitation: null, leader: true} + model.subscribe model.query('parties').withId(id), (err, party) -> + throw err if err + model.ref '_party', party.at(0) - exports.partyInvite = -> + appExports.partyInvite = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') return if _.isEmpty(id) @@ -25,42 +28,56 @@ module.exports.app = (exports, model) -> model.set "_view.partyError", "User already in a party or pending invitation." return else - model.push "parties.#{obj.party.current}.invites", id - model.set "users.#{id}.party.invitation", obj.party.current + party = model.at '_party' + party.push "invites", id + model.set "users.#{id}.party.invitation", party.get('id') $.bootstrapGrowl "Invitation Sent." $('#party-modal').modal('hide') - #model.set '_newPartyMember', '' - #window.location.reload() #TODO break old subscription, setup new subscript, remove this reload + model.subscribe model.query('uesres').party(party.get('members')), (err, members) -> + throw err if err + model.ref '_partyMembers', members + model.set '_newPartyMember', '' + #TODO break old subscription, setup new subscript, remove this reload - exports.partyAccept = -> + appExports.partyAccept = -> invitation = user.get('party.invitation') - model.push "parties.#{invitation}.members", user.get('id') - user.set 'party.invitation', null, -> - user.set 'party.current', invitation, -> - window.location.reload() + debugger + model.subscribe model.query("parties").withId(invitation), (err, parties) -> + debugger + throw err if err + party = parties.at(0) + party.push 'members', user.get('id') + user.set 'party.invitation', null + user.set 'party.current', party.get('id') + model.ref '_party', party + model.subscribe model.query('users').party(party.get('members')), (err, members) -> + debugger + throw err if err + model.ref '_partyMembers', members - exports.partyReject = -> + appExports.partyReject = -> user.set 'party.invitation', null + # TODO splice parties.*.invites[key] # TODO notify sender - exports.partyLeave = -> - id = user.get('party.current') - user.set 'party.current', null, -> - members = model.get "parties.#{id}.members" - index = members.indexOf(user.get('id')) - newMembers = members.slice(index) - model.set "parties.#{id}.members", newMembers -> - if (newMembers.length == 0) - # last member out, kill the party - model.del "parties.#{id}", -> window.location.reload() - else - window.location.reload() + appExports.partyLeave = -> + user.set 'party.current', null + party = model.at '_party' + members = party.get('members') + index = members.indexOf(user.get('id')) + newMembers = members.slice(index) + party.set 'members', newMembers + if (newMembers.length == 0) + # last member out, kill the party + model.del "parties.#{id}", -> window.location.reload() + else + window.location.reload() - exports.partyDisband = -> + #exports.partyDisband = -> user.on 'set', 'party.invitation', (id) -> - model.fetch "parties.#{id}", (err, party) -> model.set '_party', party + model.fetch model.query('parties').withId(id), (err, party) -> model.set '_party', party # # model.on '*', '_party.members', (ids) -> # # TODO unsubscribe to previous subscription diff --git a/src/server/store.coffee b/src/server/store.coffee index 07e2357af1..dda967b41d 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -11,8 +11,7 @@ module.exports.customAccessControl = (store) -> # return next(true) store.readPathAccess "users.*", -> # captures, next) -> - return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 - console.log arguments + #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 captures = arguments[0] next = arguments[arguments.length - 1] sameSession = captures is @session.userId @@ -20,7 +19,7 @@ module.exports.customAccessControl = (store) -> next sameSession or isServer store.writeAccess "*", "users.*", -> # captures, value, next) -> - return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 + #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 [captures, next] = [arguments[0].split('.'), arguments[arguments.length-1]] uid = captures.shift() attrPath = captures.join('.') # new array shifted left, after shift() was run @@ -29,10 +28,10 @@ module.exports.customAccessControl = (store) -> #return next(true) if !this.req.socket; # public access to users.*.party.invitation (TODO, lock down a bit more) - console.log attrPath return next(true) if (attrPath == 'party.invitation') # Same session (user.id = this.session.userId) + console.log {uid:uid, sess:@session.userId} return next(true) if uid is @session.userId next(false) @@ -45,7 +44,7 @@ module.exports.customAccessControl = (store) -> # next(purchasingSomethingOnClient or isServer) store.writeAccess "*", "users.*.flags.ads", -> # captures, value, next -> - return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 + #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 next = arguments[arguments.length - 1] isServer = not @req.socket next(isServer) @@ -59,7 +58,7 @@ module.exports.customAccessControl = (store) -> .limit(1) store.queryAccess "users", "withIdAndToken", (id, token, next) -> - return next(false) unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 + #return next(false) unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 isServer = not @req.socket next(isServer) @@ -88,4 +87,4 @@ module.exports.customAccessControl = (store) -> arguments[arguments.length-1](true) store.writeAccess "*", "parties.*", -> - arguments[arguments.length-1](true) + arguments[arguments.length-1](true) \ No newline at end of file From 0505b3a4d74c6761479f20abb39908435698f68c Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 18:23:56 -0500 Subject: [PATCH 12/31] typos --- src/app/index.coffee | 2 +- src/app/party.coffee | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 33b63cd5a8..70419fdff8 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -38,7 +38,7 @@ get '/', (page, model, next) -> # This used to be in party.server(model, cb), but was getting `TypeError: Object # has no method 'server'` # on the second load for some reason - selfQ = model.query('users').withId(model.session.userId) + selfQ = model.query('users').withId(model.get('_userId')) model.subscribe selfQ, (err, users) -> console.log err if err diff --git a/src/app/party.coffee b/src/app/party.coffee index 7d212f46d9..c81d72b1d9 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -33,7 +33,7 @@ module.exports.app = (appExports, model) -> model.set "users.#{id}.party.invitation", party.get('id') $.bootstrapGrowl "Invitation Sent." $('#party-modal').modal('hide') - model.subscribe model.query('uesres').party(party.get('members')), (err, members) -> + model.subscribe model.query('users').party(party.get('members')), (err, members) -> throw err if err model.ref '_partyMembers', members model.set '_newPartyMember', '' @@ -56,19 +56,21 @@ module.exports.app = (appExports, model) -> model.ref '_partyMembers', members appExports.partyReject = -> + debugger user.set 'party.invitation', null # TODO splice parties.*.invites[key] # TODO notify sender appExports.partyLeave = -> + debugger user.set 'party.current', null party = model.at '_party' members = party.get('members') index = members.indexOf(user.get('id')) - newMembers = members.slice(index) - party.set 'members', newMembers - if (newMembers.length == 0) + members.splice(index) + party.set 'members', members + if (members.length == 0) # last member out, kill the party model.del "parties.#{id}", -> window.location.reload() else @@ -77,7 +79,8 @@ module.exports.app = (appExports, model) -> #exports.partyDisband = -> user.on 'set', 'party.invitation', (id) -> - model.fetch model.query('parties').withId(id), (err, party) -> model.set '_party', party + debugger + model.subscribe model.query('parties').withId(id), (err, party) -> model.set '_party', party # # model.on '*', '_party.members', (ids) -> # # TODO unsubscribe to previous subscription From c1ac873ad514474e536309df476c2c80b02102d0 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 18:52:29 -0500 Subject: [PATCH 13/31] pretty up the html a bit --- src/app/party.coffee | 12 ++++-------- views/app/index.html | 39 +++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index c81d72b1d9..a9f9549ad1 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -41,9 +41,7 @@ module.exports.app = (appExports, model) -> appExports.partyAccept = -> invitation = user.get('party.invitation') - debugger model.subscribe model.query("parties").withId(invitation), (err, parties) -> - debugger throw err if err party = parties.at(0) party.push 'members', user.get('id') @@ -51,7 +49,6 @@ module.exports.app = (appExports, model) -> user.set 'party.current', party.get('id') model.ref '_party', party model.subscribe model.query('users').party(party.get('members')), (err, members) -> - debugger throw err if err model.ref '_partyMembers', members @@ -63,8 +60,7 @@ module.exports.app = (appExports, model) -> # TODO notify sender appExports.partyLeave = -> - debugger - user.set 'party.current', null + id = user.set 'party.current', null party = model.at '_party' members = party.get('members') index = members.indexOf(user.get('id')) @@ -72,9 +68,9 @@ module.exports.app = (appExports, model) -> party.set 'members', members if (members.length == 0) # last member out, kill the party - model.del "parties.#{id}", -> window.location.reload() - else - window.location.reload() + model.del "parties.#{id}" + model.unsubscribe model.query('parties').withId(id) + model.set('_party', null) #exports.partyDisband = -> diff --git a/views/app/index.html b/views/app/index.html index 11124cdf2e..c22759ee92 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -52,9 +52,9 @@

This resets your entire account - your tasks will be deleted and your character will start over.

This is highly discouraged because you'll lose historical data, which is useful for graphing your progress over time. However, some people find it useful in the beginning after playing with the app for a while.

<@footer> - - - + + + @@ -111,26 +111,28 @@ - -

- {#if _user.party.current}Invite Member To "{_party.name}" - {else if _user.party.invitation}You're Invited To "{_party.name}" + +

+ {#if _party}{_party.name} + {else if _user.party.invitation}You're Invited To {_party.name} {else}Create A Party{/} -

+

{#if _user.party.current} -
    +
- - + +
-
Lvl {_user.pub.stats.lvl}
+
Lvl {_user.stats.lvl}
-
- {round(_user.pub.stats.hp)} / 50 +
+ {round(_user.stats.hp)} / 50
-
+
{#if _user.history.exp}   {/} - {round(_user.pub.stats.exp)} / {_tnl} + {round(_user.stats.exp)} / {_tnl}
- - + +
-
Lvl {:member.pub.stats.lvl}
+
Lvl {:member.stats.lvl}
@@ -319,7 +319,7 @@

Rewards

-
{gold(_user.stats.money)} {silver(_user.stats.money)}
+
{gold(_user.stats.gp)} {silver(_user.stats.gp)}
From 4ac1a84acd9cfcd11077dfb919011a590129d6fb Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 01:46:46 -0500 Subject: [PATCH 08/31] kill party with last member, show user avatars in party --- src/app/party.coffee | 9 +++++++-- views/app/index.html | 20 ++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index 9ea816407a..f40df7fce9 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -14,7 +14,7 @@ module.exports.app = (exports, model) -> exports.partyCreate = -> newParty = model.get("_newParty") - id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [], invites: [] } + id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')] } user.set 'party', {current: id, invitation: null, leader: true} exports.partyInvite = -> @@ -55,7 +55,12 @@ module.exports.app = (exports, model) -> user.set 'party.current', null members = model.get ("parties.#{id}.members") index = members.indexOf(user.get('id')) - model.set "parties.#{id}.members", members.slice(index) + newMembers = members.slice(index) + if (newMembers.length == 0) + # last member out, kill the party + model.del "parties.#{id}" + else + model.set "parties.#{id}.members", members.slice(index) window.location.reload() exports.partyDisband = -> diff --git a/views/app/index.html b/views/app/index.html index 4ebdb3fa9d..11124cdf2e 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -120,9 +120,11 @@ {#if _user.party.current} +
    {#each _partyMembers as :member} - {:member.id} +
  • {username(:member.auth)}
  • {/} +
{#if _view.partyError}
{_view.partyError}
@@ -231,13 +233,15 @@ {#if _party} {#each _partyMembers as :member} -
-
- - -
-
Lvl {:member.stats.lvl}
-
+
+ + +
+
Lvl {:member.stats.lvl}
+
{#each _partyMembers as :member} -
  • {username(:member.auth)}
  • + {/} - - +
    {username(:member.auth)}({:member.id})
    + {#if _view.partyError}
    {_view.partyError}
    {/} - - +
    + + +
    Leave @@ -143,17 +145,18 @@ Reject {else} -
    + {#if _view.partyError}
    {_view.partyError}
    {/} - - +
    + + +
    {/} {/} -
    From 2169cc67405891fa81d3c21b248abf3751b8c386 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 19:05:29 -0500 Subject: [PATCH 14/31] trying to subscribe to _party.members to update _partyMembers reference --- src/app/index.coffee | 1 - src/app/party.coffee | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 70419fdff8..c7d8e64971 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -44,7 +44,6 @@ get '/', (page, model, next) -> user = users.at(0) model.ref '_user', user - obj = user.get() batch = new schema.BatchUpdate(model) batch.startTransaction() diff --git a/src/app/party.coffee b/src/app/party.coffee index a9f9549ad1..c773e3d8e1 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -78,7 +78,7 @@ module.exports.app = (appExports, model) -> debugger model.subscribe model.query('parties').withId(id), (err, party) -> model.set '_party', party # -# model.on '*', '_party.members', (ids) -> -# # TODO unsubscribe to previous subscription -# model.subscribe model.query('users').party(ids), (err, members) -> -# model.ref '_view.partyMembers', members + model.on '*', '_party.members', (ids) -> + # TODO unsubscribe to previous subscription + q = model.query('users').party(ids) + model.subscribe q, (err, members) -> model.ref '_partyMembers', members From fbeaabc7c0c5d6f1023bdfc49749edc273497735 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 19:25:46 -0500 Subject: [PATCH 15/31] sometimes model.session.userId is available, sometimes model.get('_userId'). *sigh* --- src/app/index.coffee | 9 ++++----- views/app/index.html | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index c7d8e64971..418b331062 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -38,9 +38,9 @@ get '/', (page, model, next) -> # This used to be in party.server(model, cb), but was getting `TypeError: Object # has no method 'server'` # on the second load for some reason - selfQ = model.query('users').withId(model.get('_userId')) + selfQ = model.query('users').withId(model.get('_userId') or model.session.userId) model.subscribe selfQ, (err, users) -> - console.log err if err + throw err if err user = users.at(0) model.ref '_user', user @@ -65,11 +65,10 @@ get '/', (page, model, next) -> setupModelFns(model) if obj.party?.current? - console.log obj.party.current - console.log err if err + throw err if err partiesQ = model.query('parties').withId(obj.party.current) model.subscribe partiesQ, (err, parties) -> - console.log err if err + throw err if err party = parties.at(0) model.ref '_party', party membersQ = model.query('users').party(parties.at(0).get('members')) diff --git a/views/app/index.html b/views/app/index.html index c22759ee92..93d56019fb 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -119,8 +119,7 @@ {#if _user.party.current} - - +
    {#each _partyMembers as :member} {/} From 6d9f9367c748e6231399b400aa4391500cf9ce8e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 20:21:05 -0500 Subject: [PATCH 16/31] move party suscriptions to separate function so we can re-activate when party change --- src/app/party.coffee | 24 +++++++++++++++--------- src/server/store.coffee | 1 - 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index c773e3d8e1..de88851bd1 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,8 +1,19 @@ _ = require('underscore') schema = require './schema' +setupListeners = (model) -> + + model.on 'set', '_user.party.invitation', (id) -> + model.subscribe model.query('parties').withId(id), (err, party) -> model.set '_party', party + + model.on '*', '_party.members', (ids) -> + # TODO unsubscribe to previous subscription + q = model.query('users').party(ids) + model.subscribe q, (err, members) -> model.ref '_partyMembers', members + module.exports.app = (appExports, model) -> user = model.at('_user') + setupListeners(model) appExports.partyCreate = -> newParty = model.get("_newParty") @@ -11,8 +22,10 @@ module.exports.app = (appExports, model) -> model.subscribe model.query('parties').withId(id), (err, party) -> throw err if err model.ref '_party', party.at(0) + setupListeners(model) appExports.partyInvite = -> + debugger id = model.get('_newPartyMember').replace(/[\s"]/g, '') return if _.isEmpty(id) @@ -40,6 +53,7 @@ module.exports.app = (appExports, model) -> #TODO break old subscription, setup new subscript, remove this reload appExports.partyAccept = -> + debugger invitation = user.get('party.invitation') model.subscribe model.query("parties").withId(invitation), (err, parties) -> throw err if err @@ -60,6 +74,7 @@ module.exports.app = (appExports, model) -> # TODO notify sender appExports.partyLeave = -> + debugger id = user.set 'party.current', null party = model.at '_party' members = party.get('members') @@ -73,12 +88,3 @@ module.exports.app = (appExports, model) -> model.set('_party', null) #exports.partyDisband = -> - - user.on 'set', 'party.invitation', (id) -> - debugger - model.subscribe model.query('parties').withId(id), (err, party) -> model.set '_party', party -# - model.on '*', '_party.members', (ids) -> - # TODO unsubscribe to previous subscription - q = model.query('users').party(ids) - model.subscribe q, (err, members) -> model.ref '_partyMembers', members diff --git a/src/server/store.coffee b/src/server/store.coffee index dda967b41d..07d5061be7 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -31,7 +31,6 @@ module.exports.customAccessControl = (store) -> return next(true) if (attrPath == 'party.invitation') # Same session (user.id = this.session.userId) - console.log {uid:uid, sess:@session.userId} return next(true) if uid is @session.userId next(false) From 63dc9c1305ea4b539094c9c55ea5cf16e1ac203d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 20:21:26 -0500 Subject: [PATCH 17/31] only show party in menu if unlocked --- views/app/index.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/views/app/index.html b/views/app/index.html index 93d56019fb..88b20ad1a2 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -191,7 +191,11 @@ From d3f8e361bf94ff296ffc381620b5b4aca37792cc Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 20:48:43 -0500 Subject: [PATCH 18/31] better party-unlocked message --- public/img/party-unlocked.png | Bin 0 -> 7761 bytes src/app/browser.coffee | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 public/img/party-unlocked.png diff --git a/public/img/party-unlocked.png b/public/img/party-unlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..5daba47f357ba0bcdecccc3870f9efcb4ee18bcd GIT binary patch literal 7761 zcmZvB1yEei(l721+zHO&?ry=|f`%ZAJBuuy-~@Mv1PBDr;u73pad&rj2=e&fd*Aon zdi74#%$d{OzwYUtneM4Nks509m}sPEFfcHfiVCt?Z*TdxW`%lX<-?9&OhB7cz~FCEmEs{4KsKzf3eT!4S4AWDsiWbV6g+fl2i9nM^;sNlQb^NyO~ z1W|XR#nT+1-D7<7I4ra#v?sFnX-QVh)V3~@L^mR}tTJbeE+wg$FgaH>YO-cp8GgF# zK#eV77>di@NpLrMv_TPtGJKM7`l5DA+wSvic_NZVy~Bxs!k^?GLvv)>zFrx>9r^FQ zZ3EKs75VuTKLT7CT$+D2uJ=SgKunEV0yTD=`G}wbr4>oEb>51j!ORq%cpzy**NLo| z#wwwvBGJrkY=a}twSnvP9eb9?^|48?{@?7PCX2>*k*h4XI&F~>b9L9`=gQTlRZWU! zfr$5UkCT3I-jtfxzY_gHt9$aMrWN?BNUMmx6E4N*s(g|TC4A&TDjKI;xS+*BLw}X&_Bx);K zOAw)x!U8-$2rrdC+B;SLl3J|HG|)tU=FSlL6gR-+*pkwfOgW6z?D?H!m{6&2;(#Hr z!pg@QXK@~}C6{P1K5u367=%O&o|kA9=Ga9dUox52GOsV_kl&Y;rBx$jGoSgTBvPo6 z%bncwM~+{Vo_io9w0qc$i$;AC*I@-L6t~qM5)C2x!}de7NTk2YrG4=UJV z%`qpLHC|!0zReVmKe=md`n8PCQ;U{bu;-d^GuOLPdS<$m;}&E6jLNneyqZXXVXP>Cl??dOFU8vr)6k^7(Q}mce&g%x_i&~4n3yKaOzKqi{bV>|} zk4!dgjF-Orc=_d2Vp>8py(VH8LL%~$&Y#(z<(bhR;Lq`_5(P47rg^3_Noh-9M-*x~ z`;$jM?^|VRhPM)BVl!i(#|O|>8}i*I+DU$DeS(ORRpU3AzE3LN{&RkedJDyPruah) zLaVV;t@CBOKp^IQM~{?)G}ouo8&U=$G+4?@`l-b~&^LcgTkL6^y^|W{Eu0$#P>4MH zrry)P$phDr+j{@$o}v0D{LM=e9;dMIx+AL!{Q%EqjDd^S;Q60m-?Wo1hMlYV?ehHihJ4ffXLGMWVH+tyTey1dFU5iK<_f4`!aL|r zcMKpaLMcG{S}8y(VoLK$I03wpFo1FOb|7QHdrKRrE(!+jFffGl|1?-dExL0U7}zme z9et3#s*13Mvm?8irL#GZ-P_USO&SJ9)LZy%=m-RvQF%K$IJpaZi_!c`Lila`51E68 z>R%!tdodb)RShZ`XEz`fA3GO2H;p(N6&01Jo28YomaN==;BQ}IG&UfRi!cX=mzNj2 z7Z1C$n>7cQkdP1uCpQN-H`|*8n>*MEWaiE0+D4Jk6tr# zXAh7V4b4A>{^$AkJb~V}|7*#~{XcHKImq!3hl7iqljDDMzd=R+p$f}5J2<;(yO>!3 z#rgU8c=$yBCH&vy|F!gQgodp*&_Q3;))DCB{x&OdE&n2r!1*AB-wUU&f&Xgm`&mlteO%OuLF}eBYf1m7T>acf*0cFM6U{hr-!t=% z*a))KRFiNAgZ@`pSxtU-yQ|v_O;Rs`>DgxiYYl71rz6M5Z=Dl2A-gd#VQ6$T#;=Ns zl%XM|mZbXM@VpK9T^rSwKr6+pZ2rWI1g5%;MVkcbz|zXf)XYp$)KF=%KQ7!nJiwJ^ zkISQ~cEf^tSp|mda#q{5wmFgP(MXC?Xg z#$}_av{Y$mXvp$pv2OC0HlUjex&aS^o5D|oeI16)x`Jld)ZnM9U4k@ag5RF z+0qC?i_xq6Rhd|NnCS!TnakK;u>~>U@W4e7Ny}Nue(OY+ZiTs*%e4QomB?Wi|FF+S zX+#xjz3-An{cUe7WvIMfYq=p!hiBU!7W)HwCrvNQt<3M-_^+JI`82%k(85_D>?sU6EeWsHMfAKpxi}+bu zDyJ)=oEvzE1AdyK=RT1LLoHBHFzPW>;#1Ttgw(3Ey=>!=WX|b6-X7asMXCJ6(oK03 zV|85Gu?5r2cs#Em63E20307Pjw05JfIIOmUFJJziWvPC(Md*&uF*asxIcsY{4R^yu z9UP7sbvHI-Cm)k7Q)Xk1x-DVbs7y&IH()wzyNG%+(4rTKS^NMQP8+_eS&ct=mrnCX zcyI8j$sK578_8<&R5QJcKQNFZuhf4`P6FvhInbMz3U~%4lp7l{l)XNwN>lg_tp}`Z z7c`ir6tN6j&POCbblczm%qfNE;{K{eypxNuZuX6>K3?MGUdHLq%`Bs#re?yj@7W)Q zR?9`a53}LrJY%Bna$s;1&}ObOrw$E;oz*|S8jUuYp4?`FCqdqNFjb~LDc3|~F@$XO&YF&{TvLk0W`k2w>- z6h|hzkyBF<)M67+yDCH&EbD+}g(*c>I?pQ~Q|Aeou^vn&HE5`pMQip-T%c^DUkfX< zJTJMJELN5`IM@C)TOeIt8t+iY0`GHE>%#+))*2TlqEF_xqx-ErV`p)@?3;bfGLkgb z{-}!P4Y?-D6H{^@eoT{9frxgo$$!a^z=}3ihW@%GwTBd8hshJ%x$hqUcGS@d z3qA?Cx&m3yal#SDH2cK~-!e09U>8c$mPA!b#Um(D2t93(=~dh?aa`<%q+Pi;mPgrF{XIa;=URj5qdkd|D)~OI{LpNLt_b#IoxJ>DNkx1p zI^n=jA{x`7ghn&|_TVLOn2x34Pwg-?ai&bKjOwz`(BWCIp-BKCNKGNq!kRQN*s;#)1vPe@)|$?5C5#Ah7oL@mcfE>1yM?5Q7B=>1Q?IE0jW{6QQmRXXQM|I4jw=GsFWaojOVXO zQGL&sT5bw7vxq)+ld>@%07X`Tl6l2uH&7o_Xx%ek>BL~C6is<6+%J+e(%Fn}RCA+; znJ{k($?7dBqA(iT!s`Yh3tEU)jUjLN(earN)2JW0&Tdtz!vkEwxgu@=4)0EGbFRrd zt4s@7`p*phGlf-j&AMS&SgVrkbnODg48vtl$YkMe4jY5Q5kjzXgDZs>5zt??e3u<4 zCRmS+;nRA*1HOT}cV`qbP8s^ZNb!U}5YrV?lamMHBwlPPwFsRUpu00OhfUZB_1}<8 z{#ey*>ujf2mrG@RE>=a?9biN};=E^{hV6zbPg8Bt03o%OgSdDjeWgOHrORefav$`e zwkqCqlwPyuD1ChKwIxs~JMp%p&cC3tGASMwmq(Q1#tUaP&GRV9^)-U7P^&K2Ulz_T zoK>IxEb7b_uRwo+WOmH>mvA>Ei?7bQTo<9c=UvE~lHTjSH;42T!k}tig7Q@g|EcCB zo6x$~=eRYf`C0vv)9foh8yDT~EW;Pp9p%~N2=u-gR@YQjz>EV{n=UKghDP}P%iclO zq!?boaCRrWg$>lIQ&y)Vq}$KT{bT7GfIEXDUo0!M#z63feq1sN>h8xj0rM$I9C5fi z;32tHF?5=`V8WPu%U8d@Ck=X`0&tD7XI~%QDl72zc!2>Xd(ZjL zP3n%BqI)MI(LfXd%MYM;z$rVnv{EO?SMR|4;U&gv%mR`Byx{aWl_UEzY0$4>_7)W^ z;eD#6*O$LLxxS|frz z%ws*84F674qc4uqnd?|x;#sHJaY9wt>(l}6WTqw!bTHXlAA5bW^ii+Un5kjKGs||B z%(<01i9t0{-2ZVPoy%hTdP+U5s_k*8q}Amylp#n4OGtLh6n&?0D>ey8D(`#IOB9nz z8!IRshDpiL?Aj%oYiXzA`y;mle*nmb}*d5o1&kBeLs= zm#L-}K_s^*@HUeHe@_5Q4mB}cQN;-_0W-QE2UP`DEN_39wg-fL`#eu_C|Q=6C%W2& zLNiZb{Mj3CT*mtW~{suh4iy;waA;^2GOe#p3A4JF<(B+Rp>4PivzaEpgbU8 zzS<13;<+^Vt2mc=o~S;@1T^vCr>5^|Q}J=#+$TAzHSzm(_@?tNbl?KzaJf!xkjY2R z4@8fTM^!r;;gtN{(fmIY>k|wGxCQeK0@+@NYn`w6C-jJrUJuKvQoUBe{SL$?@4Clw z)?xdyp!<>*e%1$~mVHVTK(rYYa}8MQ*-u*cuz6AG$5M1jl7_fO9lK{&qatI|;ZjwVqjyj_Y0Ti=>C}>3;Yq{V zDnmT+7Dd=uJ{fO~WlsdoS2h{=g_RF!;@9Icj+~OTUy#5vvzQY1>%nF~xrZqV-nwSx z;tcGOGAW1)@B^^6aJ|9Cd__iPZPOJT*FUGpRBlN8vR@E~L6Hm6w$u+zU~DPsO-n%G zVnbTU%&#P<)w0K|{en(~8C-!dzcUQK#<-KYSAp_={p=R^{sMIFZADr=qh~aTDX{!G zbC7(0*?p|8*2jZi%QHXo#>g=^k_{Y-CRKd(B-M%@Y+cEC=+h%tThVACRqO+RCQo}pjRwQr;8@U`pRpYae@s1s7kP0`D7iPCfLP2&Q9*`VsLRK2K)S##5y!I;uT{a7neQ9tz#{HpG~|WnlU? zMaAzpR#T93hr-yv#WBSe-S`5cSc!t(X94}yzFUJR+evbKPpjv<{8=>r){0P!MqR{w@jOTsj=L=eKX?(OX; z?{$sdo|5j|CZk=R$``{Y&m1LIBM}!egN-RgD2Q~ zXDetXZ~~UN{cYPq)4Bzo2D3xE&?CIC8knd_^%m~GVvpaj}Yog){#>wZI5T$vAgb{#bptOK_NW- z6W)e^R60A7R)sU;m-R%3@|SRbxF-yNxb<>oakg6&m>9Kzipp=Q0`tu$oeGcrvhs3> zH?FvaUmL`li_ z*oEIgYw-s`HSAHgI+#9;0)jMnwaa0S_T}sqKn^I;wlmE%z_#n+*HU^vSdopcvNu$2gyOJu(U${~Fe{jK=gv}y*;`KY1yybebMhNCRF8`9qU*6#tpUYOQ@L10i{A-fJDX31?xOV z`D7+QDGoEQ)u=4BU|VzoL-bYh`|KNb@Z})DsHgL2Pb72qi+J>vn#hTzHo~EzyoHa+ z;OPW>^&qyEqSwaf=yxK=oWpPzm6tCRo$L2%2pBU#JTaKUmjkqHG3<)VIgNO<8C&3o z)NwQk+emMu1Gimcs!r#56y#irG~Pf4N}{vaIO^Q*v?RX%qSBcqejFkuceSLJ5q1$a zhKr%=6{9WabOSooe!n&_SA+xP7`Jm>(d*5Lr%7dsP+7l$9`uo7s256UU z{&V7Dw)$v$m)=Yx39$jcrKxk!s@_A0n8jj8VT#M;ezRfXA~|7LkoxP<`D>9gt82D1 zu_|-+I$9GG?svp-NW0OLAIKi<Ksf2 zQm5*2W-mpX@m6tc)$Dx(2qTD0{eC-o7LpUY!61M6Zt&Bj z*>&)W{&ANr4qNPdoV-({I zCH}bsCWKb<6ed?%G$;st_BDn<9vKv8rUR3FaPxJHhnD4hYnc*i8dFwBkIgl}mZYlk zT>hms6vsz{ll8sOR)-f&N!w7EYqsIBMc?nylFm~N&&lhbWdxPHVzb^-;cc3^EbVa$~nade3wCGb&q@BwD2cxfn>zJA|i*^25^! z8w!V!fRmfK5kBP}xH%;0+Q0~6>hFW?6N>pf)7!YXmgf*`d_0yQ{i*ANDd1)&-U-E0 z515r-+x`8~q%ucc2W4mb+huFwmZLy%q-K5u`czO-GH^`twa+%rYy8OkPUO2a4H?3_ zg*X*S>>#W;D`kZXfcb`&tQr9nantK^<8LqCJ`A4Vx)8^gr6kE*cokawW?Zn~@VbVv zu|olMePkf(*=f5ljt8;#HnbIh8$fwwH-SU|_Y*e@mv?*GP)#g)WD>IKBNnY2Qt1MnigM zp@*4Y`C>xwJMsBZNmn}8`}2+F?~ig~R=f+jLS%f7i@YMG3IQ|)J&OnC4r6)39;G?A zzE@Cz6Isk1oZpffASuz1?1LZVFcoxeQUY{p>U6F;mBPlb5C;o+k>!dY@!{PHm_Psp~lX*vPd$%^{Hf6a7F37nqY z5mVE{D1oA8OogWIP(HKgu^O|Os_+@X@EaFJ{cZu&3q=(@JqU(s@15J^YDMjOI{yZR z>b4e*MXpp&y-P<57|Z0FlLE%KN_YelhUDH4xw2!Q;sUR{PN83F(E+*f>%OAeYMjM4 z^L~}@*Z%`C C#N7J; literal 0 HcmV?d00001 diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 80e69e88ca..83b860f3a8 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -112,16 +112,16 @@ module.exports.setupGrowlNotifications = (model) -> user.on 'set', 'flags.partyEnabled', (captures, args) -> return unless captures == true message = "Congratulations, you have unlocked the Party System! You can now group with your friends by adding their User Ids." - $('#add-party-button').popover + $('.main-avatar').popover title: "Pary System Unlocked" placement: 'bottom' trigger: 'manual' html: true content: "
    - - #{message} [Close] + + #{message} [Close]
    " - $('#add-party-button').popover 'show' + $('.main-avatar').popover 'show' # Setup listeners which trigger notifications From 7607d162f8c5af64946cda73c5532e933f466d3e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 20:57:12 -0500 Subject: [PATCH 19/31] remove all the debugger statements --- src/app/party.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index de88851bd1..b0a1c669b6 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -25,7 +25,6 @@ module.exports.app = (appExports, model) -> setupListeners(model) appExports.partyInvite = -> - debugger id = model.get('_newPartyMember').replace(/[\s"]/g, '') return if _.isEmpty(id) @@ -53,7 +52,6 @@ module.exports.app = (appExports, model) -> #TODO break old subscription, setup new subscript, remove this reload appExports.partyAccept = -> - debugger invitation = user.get('party.invitation') model.subscribe model.query("parties").withId(invitation), (err, parties) -> throw err if err @@ -67,14 +65,12 @@ module.exports.app = (appExports, model) -> model.ref '_partyMembers', members appExports.partyReject = -> - debugger user.set 'party.invitation', null # TODO splice parties.*.invites[key] # TODO notify sender appExports.partyLeave = -> - debugger id = user.set 'party.current', null party = model.at '_party' members = party.get('members') From 3ff08a49d2364449808db2670cb1c956d86c3273 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 21:09:09 -0500 Subject: [PATCH 20/31] fixing up permission system for API tokens & server-requests --- src/server/store.coffee | 66 ++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/src/server/store.coffee b/src/server/store.coffee index 07d5061be7..f7090361db 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -4,19 +4,20 @@ Setup read / write access ### module.exports.customAccessControl = (store) -> + userAccess(store) + partySystem(store) + REST(store) -# store.readPathAccess "users.*", () -> # captures, next -# next = arguments[arguments.length-1] -# return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 -# return next(true) +### + General user access +### +userAccess = (store) -> store.readPathAccess "users.*", -> # captures, next) -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 - captures = arguments[0] + uid = arguments[0] next = arguments[arguments.length - 1] - sameSession = captures is @session.userId - isServer = false #!this.req.socket; //TODO how to determine if request came from server, as in REST? - next sameSession or isServer + next (uid is @session.userId) or @req._isServer store.writeAccess "*", "users.*", -> # captures, value, next) -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 @@ -24,33 +25,33 @@ module.exports.customAccessControl = (store) -> uid = captures.shift() attrPath = captures.join('.') # new array shifted left, after shift() was run - # TODO the server can write to anything - aka, REST - #return next(true) if !this.req.socket; - # public access to users.*.party.invitation (TODO, lock down a bit more) - return next(true) if (attrPath == 'party.invitation') + if (attrPath == 'party.invitation') + return next(true) # Same session (user.id = this.session.userId) - return next(true) if uid is @session.userId + if (uid is @session.userId) or @req._isServer + return next(true) next(false) - -# store.writeAccess "*", "users.*.balance", (id, newBalance, next) -> -# return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 -# purchasingSomethingOnClient = newBalance < this.session.req._racerModel.get("users.#{id}.balance") -# isServer = not @req.socket -# next(purchasingSomethingOnClient or isServer) + store.writeAccess "*", "users.*.balance", (id, newBalance, next) -> + #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 + oldBalance = @session.req._racerModel?.get("users.#{id}.balance") || 0 + purchasingSomethingOnClient = newBalance < oldBalance + next(purchasingSomethingOnClient or @req._isServer) store.writeAccess "*", "users.*.flags.ads", -> # captures, value, next -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 next = arguments[arguments.length - 1] - isServer = not @req.socket - next(isServer) + next(@req._isServer) - ### - Get user with API token - ### + +### + REST + Get user with API token +### +REST = (store) -> store.query.expose "users", "withIdAndToken", (id, api_token) -> @where("id").equals(id) .where('preferences.api_token').equals(api_token) @@ -58,12 +59,13 @@ module.exports.customAccessControl = (store) -> store.queryAccess "users", "withIdAndToken", (id, token, next) -> #return next(false) unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 - isServer = not @req.socket - next(isServer) + next(true) # only user has id & token - ### - Party permissions - ### + +### + Party permissions +### +partySystem = (store) -> store.query.expose "users", "party", (ids) -> @where("id").within(ids) .only('stats', @@ -83,7 +85,9 @@ module.exports.customAccessControl = (store) -> next(true) store.readPathAccess "parties.*", -> - arguments[arguments.length-1](true) + next = arguments[arguments.length-1] + next(true) store.writeAccess "*", "parties.*", -> - arguments[arguments.length-1](true) \ No newline at end of file + next = arguments[arguments.length-1] + next(true) \ No newline at end of file From b6e7ebb6caa416c0a565aac98408526b2cc7fe31 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 21:28:05 -0500 Subject: [PATCH 21/31] proper accessControl testing of server routes, fix REST --- src/server/private.coffee | 1 + src/server/serverRoutes.coffee | 22 ++++++++++++---------- src/server/store.coffee | 14 +++++++------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/server/private.coffee b/src/server/private.coffee index ed73b3b468..eb22c87bb9 100644 --- a/src/server/private.coffee +++ b/src/server/private.coffee @@ -51,6 +51,7 @@ module.exports.routes = (expressApp) -> else model = req.getModel() userId = model.session.userId + req._isServer = true model.fetch "users.#{userId}", (err, user) -> model.ref '_user', "users.#{userId}" model.set('_user.balance', model.get('_user.balance')+5) diff --git a/src/server/serverRoutes.coffee b/src/server/serverRoutes.coffee index 0871499118..126720ef67 100644 --- a/src/server/serverRoutes.coffee +++ b/src/server/serverRoutes.coffee @@ -17,33 +17,33 @@ module.exports = (expressApp, root, derby) -> deprecatedMessage = 'This API is no longer supported, see https://github.com/lefnire/habitrpg/wiki/API for new protocol' expressApp.get '/:uid/up/:score?', (req, res) -> res.send(500, deprecatedMessage) expressApp.get '/:uid/down/:score?', (req, res) -> res.send(500, deprecatedMessage) - expressApp.post '/v1/users/:uid/tasks/:taskId/:direction', (req, res) -> res.send(500, deprecatedMessage) + expressApp.post '/users/:uid/tasks/:taskId/:direction', (req, res) -> res.send(500, deprecatedMessage) # ---------- v1 API ------------ ### - v1 API. Requires user-id and api_token, task-id, direction. Test with: - curl -X POST -H "Content-Type:application/json" -d '{"api_token":"{TOKEN}"}' localhost:3000/v1/users/{UID}/tasks/productivity/up + v1 API. Requires user-id and apiToken, task-id, direction. Test with: + curl -X POST -H "Content-Type:application/json" -d '{"apiToken":"{TOKEN}"}' localhost:3000/v1/users/{UID}/tasks/productivity/up ### - # TODO /v1/.. - expressApp.post '/users/:uid/tasks/:taskId/:direction', (req, res) -> + expressApp.post '/v1/users/:uid/tasks/:taskId/:direction', (req, res) -> {uid, taskId, direction} = req.params - {api_token, title, service, icon} = req.body + {apiToken, title, service, icon} = req.body console.log {params:req.params, body:req.body} if process.env.NODE_ENV == 'development' # Send error responses for improper API call - return res.send(500, 'request body "api_token" required') unless api_token + return res.send(500, 'request body "apiToken" required') unless apiToken return res.send(500, ':uid required') unless uid return res.send(500, ':taskId required') unless taskId return res.send(500, ":direction must be 'up' or 'down'") unless direction in ['up','down'] model = req.getModel() - model.fetch model.query('users').withIdAndToken(uid, api_token), (err, result) -> + req._isServer = true + model.fetch model.query('users').withIdAndToken(uid, apiToken), (err, result) -> return res.send(500, err) if err user = result.at(0) userObj = user.get() if _.isEmpty(userObj) - return res.send(500, "User with uid=#{uid}, token=#{api_token} not found. Make sure you're not using your username, but your User Id") + return res.send(500, "User with uid=#{uid}, token=#{apiToken} not found. Make sure you're not using your username, but your User Id") model.ref('_user', user) @@ -59,7 +59,9 @@ module.exports = (expressApp, root, derby) -> up: true down: true notes: "This task was created by a third-party service. Feel free to edit, it won't harm the connection to that service. Additionally, multiple services may piggy-back off this task." - } + }, (a,b,c) -> + console.log {a:a,b:b,c:c} + scoring.setModel(model) delta = scoring.score(taskId, direction) diff --git a/src/server/store.coffee b/src/server/store.coffee index f7090361db..4c051f95a8 100644 --- a/src/server/store.coffee +++ b/src/server/store.coffee @@ -17,7 +17,7 @@ userAccess = (store) -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 uid = arguments[0] next = arguments[arguments.length - 1] - next (uid is @session.userId) or @req._isServer + next (uid is @session.userId) or @session.req?._isServer store.writeAccess "*", "users.*", -> # captures, value, next) -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 @@ -30,7 +30,7 @@ userAccess = (store) -> return next(true) # Same session (user.id = this.session.userId) - if (uid is @session.userId) or @req._isServer + if (uid is @session.userId) or @session.req?._isServer return next(true) next(false) @@ -39,12 +39,12 @@ userAccess = (store) -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 oldBalance = @session.req._racerModel?.get("users.#{id}.balance") || 0 purchasingSomethingOnClient = newBalance < oldBalance - next(purchasingSomethingOnClient or @req._isServer) + next(purchasingSomethingOnClient or @session.req?._isServer) store.writeAccess "*", "users.*.flags.ads", -> # captures, value, next -> #return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 next = arguments[arguments.length - 1] - next(@req._isServer) + next(@session.req?._isServer) ### @@ -52,12 +52,12 @@ userAccess = (store) -> Get user with API token ### REST = (store) -> - store.query.expose "users", "withIdAndToken", (id, api_token) -> + store.query.expose "users", "withIdAndToken", (id, apiToken) -> @where("id").equals(id) - .where('preferences.api_token').equals(api_token) + .where('apiToken').equals(apiToken) .limit(1) - store.queryAccess "users", "withIdAndToken", (id, token, next) -> + store.queryAccess "users", "withIdAndToken", (id, apiToken, next) -> #return next(false) unless @session and @session.userId # https://github.com/codeparty/racer/issues/37 next(true) # only user has id & token From 77eb2d98e3bd5c648988778cdb695dde13633e16 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 21:52:06 -0500 Subject: [PATCH 22/31] lastCron typo --- src/app/scoring.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index cbb4701577..6ccf724138 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -188,7 +188,7 @@ score = (taskId, direction, times, batch, cron) -> ### cron = (resetDom_cb) -> today = +new Date - daysPassed = helpers.daysBetween(today, user.get('privlastCron')) + daysPassed = helpers.daysBetween(today, user.get('lastCron')) if daysPassed > 0 batch = new schema.BatchUpdate(model) batch.startTransaction() From 3d881ef37357dafb34c5dce8958ecd6ca809041f Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 22:27:19 -0500 Subject: [PATCH 23/31] bug fix on party leader leaving first --- src/app/party.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index b0a1c669b6..9e6a0db980 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -75,7 +75,7 @@ module.exports.app = (appExports, model) -> party = model.at '_party' members = party.get('members') index = members.indexOf(user.get('id')) - members.splice(index) + members.splice(index,1) party.set 'members', members if (members.length == 0) # last member out, kill the party From d8bc88dcb0bb0272504a99e26403e72808c2223b Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 5 Feb 2013 23:42:30 -0500 Subject: [PATCH 24/31] closure access to party and members query, setting up for unsubscription / resubscription --- src/app/index.coffee | 13 +++++-------- src/app/party.coffee | 13 +++++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index 418b331062..d1bc21c190 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -65,16 +65,13 @@ get '/', (page, model, next) -> setupModelFns(model) if obj.party?.current? - throw err if err - partiesQ = model.query('parties').withId(obj.party.current) - model.subscribe partiesQ, (err, parties) -> + party.partyQuery(model, obj.party.current, true).subscribe (err, res) -> throw err if err - party = parties.at(0) - model.ref '_party', party - membersQ = model.query('users').party(parties.at(0).get('members')) - model.subscribe membersQ, (err, members) -> + p = res.at(0) + model.ref '_party', p + party.membersQuery(model, p.get('members'), true).subscribe (err, res) -> throw err if err - model.ref '_partyMembers', members + model.ref '_partyMembers', res # Here's a hack we need to get fixed (hopefully Lever will) - later model.queries override previous model.queries' # returned fields. Aka, we need this here otherwise we only get the "public" fields for the current user, which diff --git a/src/app/party.coffee b/src/app/party.coffee index 9e6a0db980..532dfeb691 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,6 +1,19 @@ _ = require('underscore') schema = require './schema' +_partyQ = null +_membersQ = null + +module.exports.partyQuery = partyQuery = (model, id, reset) -> + if !_partyQ? or reset + _partyQ = model.query('parties').withId(id) + _partyQ + +module.exports.membersQuery = membersQuery = (model, ids, reset) -> + if !_membersQ? or reset + _membersQ = model.query('users').party(ids) + _membersQ + setupListeners = (model) -> model.on 'set', '_user.party.invitation', (id) -> From 6ce382af516f9fa84961d17bb5d3d21e6506b9a9 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 00:15:00 -0500 Subject: [PATCH 25/31] closure queries fully setup, but they break on page refresh. just regenerate each time for now --- src/app/index.coffee | 10 ++---- src/app/party.coffee | 81 ++++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/app/index.coffee b/src/app/index.coffee index d1bc21c190..1dd539fe53 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -65,13 +65,9 @@ get '/', (page, model, next) -> setupModelFns(model) if obj.party?.current? - party.partyQuery(model, obj.party.current, true).subscribe (err, res) -> - throw err if err - p = res.at(0) - model.ref '_party', p - party.membersQuery(model, p.get('members'), true).subscribe (err, res) -> - throw err if err - model.ref '_partyMembers', res + party.partySubscribe model, obj.party.current, (p) -> + console.log {current:obj.party.current, pMembers: p.get('members')} + party.membersSubscribe model, p.get('members'), (m) -> # Here's a hack we need to get fixed (hopefully Lever will) - later model.queries override previous model.queries' # returned fields. Aka, we need this here otherwise we only get the "public" fields for the current user, which diff --git a/src/app/party.coffee b/src/app/party.coffee index 532dfeb691..25eb4c7e1a 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,28 +1,45 @@ _ = require('underscore') schema = require './schema' -_partyQ = null -_membersQ = null +_subscriptions = + party: + query: null + id: null + members: + query: null + ids: null -module.exports.partyQuery = partyQuery = (model, id, reset) -> - if !_partyQ? or reset - _partyQ = model.query('parties').withId(id) - _partyQ +module.exports.partySubscribe = partySubscribe = (model, id, cb) -> + # New subscription coming in, or reset subscription with new parameters + if true #!_subscriptions.party.query? or _subscriptions.party.id != id + _subscriptions.party.query = model.query('parties').withId(id) + _subscriptions.party.id = id + _subscriptions.party.query.subscribe (err, p) -> + throw err if err + model.ref '_party', p.at(0) + cb(p.at(0)) if cb? + else + cb(model.at('_party')) if cb? -module.exports.membersQuery = membersQuery = (model, ids, reset) -> - if !_membersQ? or reset - _membersQ = model.query('users').party(ids) - _membersQ +module.exports.membersSubscribe = membersSubscribe = (model, ids, cb) -> + # New subscription coming in, or reset subscription with new parameters + if true #!_subscriptions.members.query? or !_.isEmpty(_.difference(_subscriptions.members.ids, ids)) + _subscriptions.members.query = model.query('users').party(ids) + _subscriptions.members.ids = ids + _subscriptions.members.query.subscribe (err, m) -> + throw err if err + model.ref '_partyMembers', m + cb(m) if cb? + else + cb(model.at('_partyMembers')) if cb? setupListeners = (model) -> model.on 'set', '_user.party.invitation', (id) -> - model.subscribe model.query('parties').withId(id), (err, party) -> model.set '_party', party + partySubscribe model, id model.on '*', '_party.members', (ids) -> - # TODO unsubscribe to previous subscription - q = model.query('users').party(ids) - model.subscribe q, (err, members) -> model.ref '_partyMembers', members + membersSubscribe model, ids module.exports.app = (appExports, model) -> user = model.at('_user') @@ -32,10 +49,7 @@ module.exports.app = (appExports, model) -> newParty = model.get("_newParty") id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } user.set 'party', {current: id, invitation: null, leader: true} - model.subscribe model.query('parties').withId(id), (err, party) -> - throw err if err - model.ref '_party', party.at(0) - setupListeners(model) + partySubscribe model, id appExports.partyInvite = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') @@ -53,29 +67,22 @@ module.exports.app = (appExports, model) -> model.set "_view.partyError", "User already in a party or pending invitation." return else - party = model.at '_party' - party.push "invites", id - model.set "users.#{id}.party.invitation", party.get('id') + p = model.at '_party' + p.push "invites", id + model.set "users.#{id}.party.invitation", p.get('id') $.bootstrapGrowl "Invitation Sent." $('#party-modal').modal('hide') - model.subscribe model.query('users').party(party.get('members')), (err, members) -> - throw err if err - model.ref '_partyMembers', members + membersSubscribe model, p.get('members') model.set '_newPartyMember', '' #TODO break old subscription, setup new subscript, remove this reload appExports.partyAccept = -> invitation = user.get('party.invitation') - model.subscribe model.query("parties").withId(invitation), (err, parties) -> - throw err if err - party = parties.at(0) - party.push 'members', user.get('id') + partySubscribe model, invitation, (p) -> + p.push 'members', user.get('id') user.set 'party.invitation', null - user.set 'party.current', party.get('id') - model.ref '_party', party - model.subscribe model.query('users').party(party.get('members')), (err, members) -> - throw err if err - model.ref '_partyMembers', members + user.set 'party.current', p.get('id') + membersSubscribe model, p.get('members'), (m) appExports.partyReject = -> user.set 'party.invitation', null @@ -85,15 +92,15 @@ module.exports.app = (appExports, model) -> appExports.partyLeave = -> id = user.set 'party.current', null - party = model.at '_party' - members = party.get('members') + p = model.at '_party' + members = p.get('members') index = members.indexOf(user.get('id')) members.splice(index,1) - party.set 'members', members + p.set 'members', members if (members.length == 0) # last member out, kill the party model.del "parties.#{id}" - model.unsubscribe model.query('parties').withId(id) + _subscriptions.party.query.unsubscribe() model.set('_party', null) #exports.partyDisband = -> From 972a7af0ff110571dc652314e3334101763c6425 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 00:47:08 -0500 Subject: [PATCH 26/31] various fixes and page-refresh fallbacks --- src/app/party.coffee | 43 ++++++++++++++++++++++-------------------- views/app/index.html | 45 ++++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index 25eb4c7e1a..8d325fcd73 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -14,10 +14,14 @@ module.exports.partySubscribe = partySubscribe = (model, id, cb) -> if true #!_subscriptions.party.query? or _subscriptions.party.id != id _subscriptions.party.query = model.query('parties').withId(id) _subscriptions.party.id = id - _subscriptions.party.query.subscribe (err, p) -> + _subscriptions.party.query.subscribe (err, res) -> throw err if err - model.ref '_party', p.at(0) - cb(p.at(0)) if cb? + p = res.at(0) + model.ref '_party', p + p.on '*', 'members', (ids) -> + # FIXME not being triggered... + membersSubscribe model, ids + cb(p) if cb? else cb(model.at('_party')) if cb? @@ -33,18 +37,12 @@ module.exports.membersSubscribe = membersSubscribe = (model, ids, cb) -> else cb(model.at('_partyMembers')) if cb? -setupListeners = (model) -> +module.exports.app = (appExports, model) -> + user = model.at('_user') model.on 'set', '_user.party.invitation', (id) -> partySubscribe model, id - model.on '*', '_party.members', (ids) -> - membersSubscribe model, ids - -module.exports.app = (appExports, model) -> - user = model.at('_user') - setupListeners(model) - appExports.partyCreate = -> newParty = model.get("_newParty") id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } @@ -57,13 +55,13 @@ module.exports.app = (appExports, model) -> obj = user.get() query = model.query('users').party([id]) - model.fetch query, (err, users) -> + model.fetch query, (err, res) -> throw err if err - partyMember = users.at(0).get() - if !partyMember?.id? + u = res.at(0).get() + if !u?.id? model.set "_view.partyError", "User with id #{id} not found." return - else if partyMember.party.current?.id? or partyMember.party.invitation? + else if u.party.current? or u.party.invitation? model.set "_view.partyError", "User already in a party or pending invitation." return else @@ -72,9 +70,10 @@ module.exports.app = (appExports, model) -> model.set "users.#{id}.party.invitation", p.get('id') $.bootstrapGrowl "Invitation Sent." $('#party-modal').modal('hide') - membersSubscribe model, p.get('members') model.set '_newPartyMember', '' - #TODO break old subscription, setup new subscript, remove this reload + membersSubscribe model, p.get('members'), -> + # TODO get subscriptions really working so we don't need to reload + window.location.reload() appExports.partyAccept = -> invitation = user.get('party.invitation') @@ -82,10 +81,12 @@ module.exports.app = (appExports, model) -> p.push 'members', user.get('id') user.set 'party.invitation', null user.set 'party.current', p.get('id') - membersSubscribe model, p.get('members'), (m) + membersSubscribe model, p.get('members'), (m) -> + window.location.reload() appExports.partyReject = -> user.set 'party.invitation', null + model.set '_party', null # TODO splice parties.*.invites[key] # TODO notify sender @@ -100,7 +101,9 @@ module.exports.app = (appExports, model) -> if (members.length == 0) # last member out, kill the party model.del "parties.#{id}" - _subscriptions.party.query.unsubscribe() - model.set('_party', null) + #_subscriptions.party.query.unsubscribe() + model.set '_party', null + model.set '_partyMembers', null + window.location.reload() #exports.partyDisband = -> diff --git a/views/app/index.html b/views/app/index.html index 88b20ad1a2..84412e1889 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -112,13 +112,9 @@ -

    - {#if _party}{_party.name} - {else if _user.party.invitation}You're Invited To {_party.name} - {else}Create A Party{/} -

    - {#if _user.party.current} + {#if _user.party.current} +

    {_party.name}

    {username(:member.auth)}({:member.id})
    {#each _partyMembers as :member} @@ -135,26 +131,25 @@ Leave - {else} + {else if _user.party.invitation} + +

    You're Invited To {_party.name}

    + Accept + Reject - - {#if _user.party.invitation} - - Accept - Reject - {else} - - - {#if _view.partyError} -
    {_view.partyError}
    - {/} -
    - - -
    - - {/} - {/} + {else} +

    Create A Party

    + + + {#if _view.partyError} +
    {_view.partyError}
    + {/} +
    + + +
    + + {/} From 427e2983cf57b26213f94c7a1df6868fc453451d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 01:11:12 -0500 Subject: [PATCH 27/31] some setup and comments for unsubscribe, for when we get that fixed in the future --- src/app/party.coffee | 71 +++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/src/app/party.coffee b/src/app/party.coffee index 8d325fcd73..389c082301 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -10,32 +10,55 @@ _subscriptions = ids: null module.exports.partySubscribe = partySubscribe = (model, id, cb) -> - # New subscription coming in, or reset subscription with new parameters - if true #!_subscriptions.party.query? or _subscriptions.party.id != id - _subscriptions.party.query = model.query('parties').withId(id) - _subscriptions.party.id = id - _subscriptions.party.query.subscribe (err, res) -> - throw err if err - p = res.at(0) - model.ref '_party', p - p.on '*', 'members', (ids) -> - # FIXME not being triggered... - membersSubscribe model, ids - cb(p) if cb? - else - cb(model.at('_party')) if cb? + s = _subscriptions + + ### + # Note, this tries to unsubscribe from previous similar subscriptions so we don't have a memory leak. However, + # This causes the page to crash on refresh. We need to fix this in the future. Same goes for membersSubscribe + if s.party.query? and id == s.party.id + # No need to resubscribe, same parameters + return cb(model.at('_party')) + + # already have a subscription, but we want a new one + if s.party.query? and s.party.id != id + s.party.query.unsubscribe() + s.party.query = null + ### + + # subscripe + s.party.query = model.query('parties').withId(id) + s.party.id = id + s.party.query.subscribe (err, res) -> + throw err if err + p = res.at(0) + model.ref '_party', p + p.on '*', 'members', (ids) -> + # FIXME not being triggered... + membersSubscribe model, ids + cb(p) if cb? + module.exports.membersSubscribe = membersSubscribe = (model, ids, cb) -> - # New subscription coming in, or reset subscription with new parameters - if true #!_subscriptions.members.query? or !_.isEmpty(_.difference(_subscriptions.members.ids, ids)) - _subscriptions.members.query = model.query('users').party(ids) - _subscriptions.members.ids = ids - _subscriptions.members.query.subscribe (err, m) -> - throw err if err - model.ref '_partyMembers', m - cb(m) if cb? - else - cb(model.at('_partyMembers')) if cb? + s = _subscriptions + + ### @see above + if s.members.query? and !_.isEmpty(_.difference(s.members.ids, ids)) + # No need to resubscribe, same parameters + return cb(model.at('_partyMembers')) + + # already have a subscription, but we want a new one + if s.members.query? and _.isEmpty(_.difference(s.members.ids, ids)) + s.members.query.unsubscribe() + s.members.query = null + ### + + # subscripe + s.members.query = model.query('users').party(ids) + s.members.ids = ids + s.members.query.subscribe (err, m) -> + throw err if err + model.ref '_partyMembers', m + cb(m) if cb? module.exports.app = (appExports, model) -> user = model.at('_user') From a0c4480c53acfebf15c9514bbd0e1e5714373a35 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 01:59:02 -0500 Subject: [PATCH 28/31] cleanup, typos, html tidying --- src/app/helpers.coffee | 3 ++- src/app/index.coffee | 2 -- src/app/party.coffee | 13 +++++++++---- views/app/index.html | 28 +++++++++++++--------------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index 20d2f446a9..501a1b6e73 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -50,8 +50,9 @@ module.exports.viewHelpers = (view) -> else return "0.00" - view.fn "lessThan", (a, b) -> + view.fn "lt", (a, b) -> a < b + view.fn 'gt', (a, b) -> a > b view.fn "tokens", (gp) -> return gp/0.25 diff --git a/src/app/index.coffee b/src/app/index.coffee index 1dd539fe53..894143eede 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -66,8 +66,6 @@ get '/', (page, model, next) -> if obj.party?.current? party.partySubscribe model, obj.party.current, (p) -> - console.log {current:obj.party.current, pMembers: p.get('members')} - party.membersSubscribe model, p.get('members'), (m) -> # Here's a hack we need to get fixed (hopefully Lever will) - later model.queries override previous model.queries' # returned fields. Aka, we need this here otherwise we only get the "public" fields for the current user, which diff --git a/src/app/party.coffee b/src/app/party.coffee index 389c082301..1d0582fd60 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -32,10 +32,15 @@ module.exports.partySubscribe = partySubscribe = (model, id, cb) -> throw err if err p = res.at(0) model.ref '_party', p + + # FIXME this is the kicker right here. This isn't getting triggered, and it's the reason why we have to refresh + # after every event. Get this working p.on '*', 'members', (ids) -> - # FIXME not being triggered... + console.log("members listener got called") membersSubscribe model, ids - cb(p) if cb? + + membersSubscribe model, p.get('members'), (m) -> + cb(p) if cb? module.exports.membersSubscribe = membersSubscribe = (model, ids, cb) -> @@ -64,7 +69,7 @@ module.exports.app = (appExports, model) -> user = model.at('_user') model.on 'set', '_user.party.invitation', (id) -> - partySubscribe model, id + partySubscribe(model, id) if id? appExports.partyCreate = -> newParty = model.get("_newParty") @@ -127,6 +132,6 @@ module.exports.app = (appExports, model) -> #_subscriptions.party.query.unsubscribe() model.set '_party', null model.set '_partyMembers', null - window.location.reload() + setTimeout window.location.reload, 1 #exports.partyDisband = -> diff --git a/views/app/index.html b/views/app/index.html index 84412e1889..a5880d1c1d 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -103,7 +103,7 @@

    Highly discouraged because red tasks provide good incentive to improve (read more). However, this becomes necessary after long bouts of bad habits.

    <@footer> - {#if lessThan(_user.balance,1)} + {#if lt(_user.balance,1)} Buy More TokensNot enough tokens {else} Re-Roll4 Tokens @@ -200,7 +200,7 @@
    -
    +
    {username(:member.auth)}({:member.id})
    @@ -214,7 +214,7 @@ - - {#if _party} - {#each _partyMembers as :member} - {#unless equal(:member.id, _userId)} - - {/} - {/} + {#each _partyMembers as :member} + {#unless equal(:member.id, _userId)} + + {/} {/} From 0e6d08e16bc7c5ba37b6b7a90bea8dc8567eee4a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 02:42:48 -0500 Subject: [PATCH 29/31] add more debug tools, and email me on error --- server.js | 36 ++++++++++++++++++++++++++++++++++-- src/app/debug.coffee | 13 +++++++++++++ src/app/index.coffee | 9 ++------- src/app/schema.coffee | 5 +---- views/app/index.html | 5 +++-- 5 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 src/app/debug.coffee diff --git a/server.js b/server.js index 51b8a7527c..d1bb7a7582 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,38 @@ -/*process.on('uncaughtException', function (error) { +process.on('uncaughtException', function (error) { + + function sendEmail(mailData) { + var nodemailer = require("derby-auth/node_modules/nodemailer"); + + // create reusable transport method (opens pool of SMTP connections) + // TODO derby-auth isn't currently configurable here, if you need customizations please send pull request + var smtpTransport = nodemailer.createTransport("SMTP",{ + service: process.env.SMTP_SERVICE, + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS + } + }); + + // send mail with defined transport object + smtpTransport.sendMail(mailData, function(error, response){ + if(error){ + console.log(error); + }else{ + console.log("Message sent: " + response.message); + } + + smtpTransport.close(); // shut down the connection pool, no more messages + }); + } + + sendEmail({ + from: "HabitRPG ", + to: "tylerrenelle@gmail.com", + subject: "HabitRPG Error", + text: error.stack + }); console.log(error.stack); -});*/ +}); require('coffee-script') // remove intermediate compilation requirement require('./src/server').listen(process.env.PORT || 3000); diff --git a/src/app/debug.coffee b/src/app/debug.coffee new file mode 100644 index 0000000000..25cb833b8f --- /dev/null +++ b/src/app/debug.coffee @@ -0,0 +1,13 @@ +moment = require 'moment' + +module.exports.app = (appExports, model) -> + user = model.at('_user') + + appExports.emulateNextDay = -> + yesterday = +moment().subtract('days', 1).toDate() + user.set 'lastCron', yesterday + window.location.reload() + + appExports.cheat = -> + user.incr 'stats.exp', 20 + user.incr 'stats.gp', 1000 diff --git a/src/app/index.coffee b/src/app/index.coffee index 894143eede..8e851e3e8f 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -105,6 +105,8 @@ ready (model) -> require('../server/private').app(exports, model) + require('./debug').app(exports, model) + user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) -> return if passed? && passed.cron # Don't do this stuff on cron direction = () -> @@ -295,10 +297,3 @@ ready (model) -> exports.setFemale = -> user.set('preferences.gender', 'f') exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1') exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2') - - exports.emulateNextDay = -> - yesterday = +moment().subtract('days', 1).toDate() - user.set 'lastCron', yesterday - window.location.reload() - - diff --git a/src/app/schema.coffee b/src/app/schema.coffee index acfd0bbf44..76658e5263 100644 --- a/src/app/schema.coffee +++ b/src/app/schema.coffee @@ -7,10 +7,7 @@ derby = require 'derby' userSchema = # _id stats: { gp: 0, exp: 0, lvl: 1, hp: 50 } - party: { - current: null - invitation: null - } + party: { current: null, invitation: null } items: { armor: 0, weapon: 0 } preferences: { gender: 'm', armorSet: 'v1' } idLists: diff --git a/views/app/index.html b/views/app/index.html index a5880d1c1d..8f9a3fb609 100644 --- a/views/app/index.html +++ b/views/app/index.html @@ -349,9 +349,10 @@
    - + +
    • Copyright © 2012 OCDevel LLC
    • From 776444735588eaf763b0bcc6b9cc83d0198f7f6d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 13:59:56 -0500 Subject: [PATCH 30/31] many fixes to party system actions causing dom refresh --- src/app/browser.coffee | 6 ++++++ src/app/index.coffee | 28 +++++++--------------------- src/app/party.coffee | 41 +++++++++++++++++++++++++++-------------- src/app/scoring.coffee | 4 ++-- 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 83b860f3a8..98235bea3c 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -1,5 +1,11 @@ content = require('./content') + +module.exports.resetDom = (model) -> + window.DERBY.app.dom.clear() + window.DERBY.app.view.render(model) + model.fn '_tnl', '_user.stats.lvl', (lvl) -> (lvl*100)/5 + ### Loads JavaScript files from (1) public/js/* and (2) external sources We use this file (instead of or inside .html) so we can utilize require() to concatinate for diff --git a/src/app/index.coffee b/src/app/index.coffee index 8e851e3e8f..9833b17ad5 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -19,11 +19,6 @@ setupListReferences = (model) -> taskTypes = ['habit', 'daily', 'todo', 'reward'] _.each taskTypes, (type) -> model.refList "_#{type}List", "_user.tasks", "_user.idLists.#{type}" -setupModelFns = (model) -> - model.fn '_tnl', '_user.stats.lvl', (lvl) -> - # see https://github.com/lefnire/habitrpg/issues/4 - # also update in scoring.coffee. TODO create a function accessible in both locations - (lvl*100)/5 # ========== ROUTES ========== @@ -62,27 +57,18 @@ get '/', (page, model, next) -> batch.commit() setupListReferences(model) - setupModelFns(model) + model.fn '_tnl', '_user.stats.lvl', (lvl) -> + # see https://github.com/lefnire/habitrpg/issues/4 + # also update in scoring.coffee. TODO create a function accessible in both locations + (lvl*100)/5 if obj.party?.current? - party.partySubscribe model, obj.party.current, (p) -> - - # Here's a hack we need to get fixed (hopefully Lever will) - later model.queries override previous model.queries' - # returned fields. Aka, we need this here otherwise we only get the "public" fields for the current user, which - # are defined in model.query('users')party() - model.subscribe selfQ, (err, users) -> - model.ref '_user', users.at(0) - page.render() + party.partySubscribe model, obj.party.current, (p) -> page.render() else page.render() # ========== CONTROLLER FUNCTIONS ========== -resetDom = (model) -> - window.DERBY.app.dom.clear() - view.render(model) - setupModelFns(model) - ready (model) -> user = model.at('_user') scoring.setModel(model) @@ -92,7 +78,7 @@ ready (model) -> user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new') # Setup model in scoring functions - scoring.cron(resetDom) + scoring.cron() # Load all the jQuery, Growl, Tour, etc browser.loadJavaScripts(model) @@ -289,7 +275,7 @@ ready (model) -> batch.set 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens revive(batch, true) batch.commit() - resetDom(model) + browser.resetDom(model) exports.closeKickstarterNofitication = (e, el) -> user.set('flags.kickstarter', 'hide') diff --git a/src/app/party.coffee b/src/app/party.coffee index 1d0582fd60..e66c84baa1 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,5 +1,6 @@ _ = require('underscore') schema = require './schema' +browser = require './browser' _subscriptions = party: @@ -35,11 +36,16 @@ module.exports.partySubscribe = partySubscribe = (model, id, cb) -> # FIXME this is the kicker right here. This isn't getting triggered, and it's the reason why we have to refresh # after every event. Get this working - p.on '*', 'members', (ids) -> - console.log("members listener got called") - membersSubscribe model, ids - - membersSubscribe model, p.get('members'), (m) -> +# p.on '*', 'members', (ids) -> +# console.log("members listener got called") +# membersSubscribe model, ids + ids = p.get('members') + if !_.isEmpty(ids) + membersSubscribe model, ids, (m) -> + browser.resetDom(model) if window? + cb(p) if cb? + else + browser.resetDom(model) if window? cb(p) if cb? @@ -63,7 +69,14 @@ module.exports.membersSubscribe = membersSubscribe = (model, ids, cb) -> s.members.query.subscribe (err, m) -> throw err if err model.ref '_partyMembers', m - cb(m) if cb? + + # Here's a hack we need to get fixed (hopefully Lever will) - later model.queries override previous model.queries' + # returned fields. Aka, we need this here otherwise we only get the "public" fields for the current user, which + # are defined in model.query('users')party() + selfQ = model.query('users').withId(model.get('_userId') or model.session.userId) + model.subscribe selfQ, (err, users) -> + model.ref '_user', users.at(0) + cb(m) if cb? module.exports.app = (appExports, model) -> user = model.at('_user') @@ -75,7 +88,7 @@ module.exports.app = (appExports, model) -> newParty = model.get("_newParty") id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [user.get('id')], invites:[] } user.set 'party', {current: id, invitation: null, leader: true} - partySubscribe model, id + partySubscribe model, id, -> $('#party-modal').modal('show') appExports.partyInvite = -> id = model.get('_newPartyMember').replace(/[\s"]/g, '') @@ -100,8 +113,7 @@ module.exports.app = (appExports, model) -> $('#party-modal').modal('hide') model.set '_newPartyMember', '' membersSubscribe model, p.get('members'), -> - # TODO get subscriptions really working so we don't need to reload - window.location.reload() + #window.location.reload(true) appExports.partyAccept = -> invitation = user.get('party.invitation') @@ -110,12 +122,12 @@ module.exports.app = (appExports, model) -> user.set 'party.invitation', null user.set 'party.current', p.get('id') membersSubscribe model, p.get('members'), (m) -> - window.location.reload() + window.location.reload(true) appExports.partyReject = -> user.set 'party.invitation', null model.set '_party', null - + browser.resetDom(model) # TODO splice parties.*.invites[key] # TODO notify sender @@ -130,8 +142,9 @@ module.exports.app = (appExports, model) -> # last member out, kill the party model.del "parties.#{id}" #_subscriptions.party.query.unsubscribe() - model.set '_party', null - model.set '_partyMembers', null - setTimeout window.location.reload, 1 + #model.set '_party', null + #model.set '_partyMembers', null + #browser.resetDom() + setTimeout (-> window.location.reload true), 1 #exports.partyDisband = -> diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 6ccf724138..2ef8b1611d 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -186,7 +186,7 @@ score = (taskId, direction, times, batch, cron) -> At end of day, add value to all incomplete Daily & Todo tasks (further incentive) For incomplete Dailys, deduct experience ### -cron = (resetDom_cb) -> +cron = () -> today = +new Date daysPassed = helpers.daysBetween(today, user.get('lastCron')) if daysPassed > 0 @@ -245,7 +245,7 @@ cron = (resetDom_cb) -> batch.setStats() batch.set('history', obj.history) batch.commit() - resetDom_cb(model) + require('./browser').reset(model) setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss From 9854bc1a3c1932a9c4ddf36201b8df27e0ca6f4c Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 6 Feb 2013 14:12:05 -0500 Subject: [PATCH 31/31] some typo fixes --- src/app/browser.coffee | 4 ++-- src/app/index.coffee | 2 +- src/app/scoring.coffee | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 98235bea3c..50b652158a 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -1,5 +1,5 @@ content = require('./content') - +_ = require 'underscore' module.exports.resetDom = (model) -> window.DERBY.app.dom.clear() @@ -60,7 +60,7 @@ module.exports.setupSortable = (model) -> # Also, note that refList index arguments can either be an index # or the item's id property model.at("_#{type}List").pass(ignore: domId).move {id}, to - setupSortable(type) for type in ['habit', 'daily', 'todo', 'reward'] + _.each ['habit', 'daily', 'todo', 'reward'], (type) -> setupSortable(type) module.exports.setupTooltips = (model) -> $('[rel=tooltip]').tooltip() diff --git a/src/app/index.coffee b/src/app/index.coffee index 9833b17ad5..fb4a4ebdca 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -273,7 +273,7 @@ ready (model) -> batch.set 'tasks', {} _.each taskTypes, (type) -> batch.set "idLists.#{type}", [] batch.set 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens - revive(batch, true) + revive(batch) batch.commit() browser.resetDom(model) diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 2ef8b1611d..c392c811bf 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -245,7 +245,7 @@ cron = () -> batch.setStats() batch.set('history', obj.history) batch.commit() - require('./browser').reset(model) + browser.resetDom(model) setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss
    +
    {round(_user.stats.hp)} / 50 @@ -232,18 +232,16 @@
    -
    - - -
    -
    Lvl {:member.stats.lvl}
    -
    +
    + + +
    +
    Lvl {:member.stats.lvl}
    +