diff --git a/website/src/models/challenge.js b/website/src/models/challenge.js index d44b798bc0..a59f0d0939 100644 --- a/website/src/models/challenge.js +++ b/website/src/models/challenge.js @@ -10,10 +10,10 @@ var ChallengeSchema = new Schema({ shortName: String, description: String, official: {type: Boolean,'default':false}, - habits: [TaskSchemas.HabitSchema], - dailys: [TaskSchemas.DailySchema], - todos: [TaskSchemas.TodoSchema], - rewards: [TaskSchemas.RewardSchema], + //habits: [TaskSchemas.HabitSchema], + //dailys: [TaskSchemas.DailySchema], + //todos: [TaskSchemas.TodoSchema], + //rewards: [TaskSchemas.RewardSchema], leader: {type: String, ref: 'User'}, group: {type: String, ref: 'Group'}, timestamp: {type: Date, 'default': Date.now}, diff --git a/website/src/models/task.js b/website/src/models/task.js index 98fd6bf3c7..de488a3d86 100644 --- a/website/src/models/task.js +++ b/website/src/models/task.js @@ -5,9 +5,10 @@ import baseModel from '../libs/api-v3/baseModel'; import _ from 'lodash'; let Schema = mongoose.Schema; -let discriminatorOptions = () => { - return {discriminatorKey: 'type'}; // the key that distinguishes task types +let discriminatorOptions = { + discriminatorKey: 'type', // the key that distinguishes task types }; +let subDiscriminatorOptions = _.defaults(_.cloneDeep(discriminatorOptions), {_id: false}); // TODO make sure a task can only update the fields belonging to its type // We could use discriminators but it looks like when loading from the parent @@ -29,10 +30,10 @@ export let TaskSchema = new Schema({ broken: String, // CHALLENGE_DELETED, TASK_DELETED, UNSUBSCRIBED, CHALLENGE_CLOSED TODO enum winner: String, // user.profile.name TODO necessary? }, -}, _.default({ +}, _.defaults({ minimize: true, // So empty objects are returned strict: true, -}, discriminatorOptions())); +}, discriminatorOptions)); TaskSchema.plugin(baseModel, { noSet: [], @@ -66,7 +67,7 @@ let dailyTodoSchema = () => { export let Habit = Task.discriminator('Habit', new Schema(_.defaults({ up: {type: Boolean, default: true}, down: {type: Boolean, default: true}, -}, habitDailySchema())), discriminatorOptions()); +}, habitDailySchema()), subDiscriminatorOptions)); export let Daily = Task.discriminator('Daily', new Schema(_.defaults({ frequency: {type: String, default: 'weekly', enum: ['daily', 'weekly']}, @@ -87,13 +88,13 @@ export let Daily = Task.discriminator('Daily', new Schema(_.defaults({ su: {type: Boolean, default: true}, }, streak: {type: Number, default: 0}, -}, habitDailySchema(), dailyTodoSchema())), discriminatorOptions()); +}, habitDailySchema(), dailyTodoSchema()), subDiscriminatorOptions)); export let Todo = Task.discriminator('Todo', new Schema(_.defaults({ dateCompleted: Date, // FIXME we're getting parse errors, people have stored as "today" and "3/13". Need to run a migration & put this back to type: Date // TODO change field name date: String, // due date for todos -}, dailyTodoSchema())), discriminatorOptions()); +}, dailyTodoSchema()), subDiscriminatorOptions)); -export let Reward = Task.discriminator('Reward', new Schema({}), discriminatorOptions()); +export let Reward = Task.discriminator('Reward', new Schema({}, subDiscriminatorOptions)); diff --git a/website/src/models/user.js b/website/src/models/user.js index 5756419195..d650dc29ba 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -3,6 +3,7 @@ import shared from '../../../common'; import _ from 'lodash'; import validator from 'validator'; import moment from 'moment'; +import Q from 'q'; import { model as Task } from './task'; import baseModel from '../libs/api-v3/baseModel'; // import {model as Challenge} from './challenge'; @@ -491,36 +492,37 @@ schema.post('init', function postInitUser (doc) { }); function _populateDefaultTasks (user, taskTypes) { - _.each(taskTypes, (taskType) => { - // TODO save in own documents - user[taskType] = _.map(shared.content.userDefaults[taskType], (taskDefaults) => { - let newTask; + if ('tags' in taskTypes) { + user.tags = _.map(shared.content.userDefaults.tags, function(tag){ + let newTag = _.cloneDeep(tag); - // Render task's text and notes in user's language - if (taskType === 'tags') { - newTask = _.cloneDeep(taskDefaults); - // tasks automatically get id=helpers.uuid() from TaskSchema id.default, but tags are Schema.Types.Mixed - so we need to manually invoke here - newTask.id = shared.uuid(); - newTask.name = newTask.name(user.preferences.language); - } else { - newTask = new Task(taskDefaults); - newTask.userId = user._id; - newTask.text = newTask.text(user.preferences.language); - if (newTask.notes) { - newTask.notes = newTask.notes(user.preferences.language); - } - - if (newTask.checklist) { - newTask.checklist = _.map(newTask.checklist, (checklistItem) => { - checklistItem.text = checklistItem.text(user.preferences.language); - return checklistItem; - }); - } - } - - return newTask; + // tasks automatically get _id=helpers.uuid() from TaskSchema id.default, but tags are Schema.Types.Mixed - so we need to manually invoke here + newTag.id = shared.uuid(); + // Render tag's name in user's language + newTag.name = newTag.name(user.preferences.language); + return newTag; }); + } + + let tasksToCreate = []; + _.each(taskTypes, (taskType) => { + let tasksOfType = _.map(shared.content.userDefaults[taskType], (taskDefaults) => { + let newTask = new Task(taskDefaults); + newTask.userId = user._id; + newTask.text = newTask.text(user.preferences.language); + if (newTask.notes) newTask.notes = newTask.notes(user.preferences.language); + if (newTask.checklist) { + newTask.checklist = _.map(newTask.checklist, (checklistItem) => { + checklistItem.text = checklistItem.text(user.preferences.language); + return checklistItem; + }); + } + }); + + tasksToCreate.push(...tasksOfType); }); + + return Task.create(tasksToCreate); } function _populateDefaultsForNewUser (user) { @@ -543,7 +545,7 @@ function _populateDefaultsForNewUser (user) { }); } - _populateDefaultTasks(user, taskTypes); + return _populateDefaultTasks(user, taskTypes); } function _setProfileName (user) { @@ -556,13 +558,10 @@ function _setProfileName (user) { return localUsername || facebookUsername || anonymous; } -schema.pre('save', function postSaveUser (next) { - // Populate new users with default content - if (this.isNew) { - _populateDefaultsForNewUser(this); - } +schema.pre('save', function postSaveUser (next, done) { + next(); - // this.markModified('tasks'); + // TODO remove all unnecessary checks if (_.isNaN(this.preferences.dayStart) || this.preferences.dayStart < 0 || this.preferences.dayStart > 23) { this.preferences.dayStart = 0; } @@ -611,7 +610,12 @@ schema.pre('save', function postSaveUser (next) { if (_.isNaN(this._v) || !_.isNumber(this._v)) this._v = 0; this._v++; - next(); + // Populate new users with default content + if (this.isNew) { + _populateDefaultsForNewUser(this) + .then((tasks) => done()) + .catch(done); + } }); schema.methods.unlink = function unlink (options, cb) {