Skip to content

Commit a3507d2

Browse files
committed
more work on courses, refactored photo uploading, fixed minor bug
1 parent 2667c00 commit a3507d2

22 files changed

Lines changed: 732 additions & 271 deletions

File tree

client/head/modal.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Modal.prototype.render = function() {
3232
Modal.prototype.onClick = function(event) {
3333
if (event.target.classList.contains('modal__close')) {
3434
this.remove();
35+
event.preventDefault();
3536
}
3637
};
3738

handlers/courses/client/index.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ exports.init = function() {
2020
function initSignupWidget() {
2121

2222
var signupWidget = document.querySelector('[data-elem="signup"]');
23-
if (signupWidget) {
24-
new SignupWidget({
25-
elem: signupWidget
26-
});
27-
}
23+
if (!signupWidget) return;
24+
25+
new SignupWidget({
26+
elem: signupWidget
27+
});
2828
}
2929

3030
function initNewsletterForm() {
3131

3232
var form = document.querySelector('[data-newsletter-subscribe-form]');
33+
if (!form) return;
3334

3435
form.onsubmit = function(event) {
3536
event.preventDefault();
@@ -41,6 +42,7 @@ function initNewsletterForm() {
4142
function initSignupButton() {
4243

4344
var link = document.querySelector('[data-group-signup-link]');
45+
if (!link) return;
4446

4547
link.onclick = function(e) {
4648

handlers/courses/controller/invite.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const CourseGroup = require('../models/courseGroup');
44
const User = require('users').User;
55
const _ = require('lodash');
66

7+
const LOGIN_SUCCESSFUL = 1;
8+
const LOGGED_IN_ALREADY = 2;
9+
const NO_SUCH_USER = 3;
10+
711
exports.all = function*() {
812

913
if (this.method != 'POST' && this.method != 'GET') {
@@ -61,23 +65,30 @@ exports.all = function*() {
6165

6266
var isLoggedIn = yield* loginByInvite.call(this, invite);
6367

64-
if (isLoggedIn) {
65-
yield* askCourseName.call(this, invite);
66-
} else {
68+
if (isLoggedIn == NO_SUCH_USER) {
6769
if (this.user) this.logout();
6870
yield* register.call(this, invite);
71+
72+
} else {
73+
if (isLoggedIn == LOGIN_SUCCESSFUL) {
74+
this.locals.wasLoggedIn = true;
75+
}
76+
yield* askParticipantDetails.call(this, invite);
6977
}
7078

7179
};
7280

73-
function* askCourseName(invite) {
81+
function* askParticipantDetails(invite) {
7482

7583
// NB: this.user is the right user, guaranteed by loginByInvite
7684

7785
if (this.method == 'POST') {
7886
yield acceptParticipant.call(this, invite);
7987
} else {
80-
this.body = this.render('invite/askCourseName', {
88+
89+
this.locals.title = "Анкета участника\n" + invite.group.title;
90+
91+
this.body = this.render('invite/askParticipantDetails', {
8192
errors: {},
8293
form: {}
8394
});
@@ -110,16 +121,15 @@ function* register(invite) {
110121
errors: e.errors,
111122
form: {
112123
displayName: this.request.body.displayName,
113-
courseName: this.request.body.courseName,
114124
password: this.request.body.password
115125
}
116126
});
117127
return;
118128
}
119129

120130
yield this.login(user);
121-
yield acceptParticipant.call(this, invite);
122131

132+
this.redirect('/courses/invite/' + invite.token);
123133

124134
} else {
125135
this.body = this.render('invite/register', {
@@ -147,17 +157,23 @@ function* acceptParticipant(invite) {
147157
this.redirect('/courses/invite/' + invite.token);
148158
}
149159

160+
/**
161+
* Logs in the current user using invite data
162+
* Makes email verified
163+
* @param invite
164+
* @returns LOGIN_SUCCESSFUL / NO_SUCH_USER / LOGGED_IN_ALREADY
165+
*/
150166
function* loginByInvite(invite) {
151167

152168
if (this.user && this.user.email == invite.email) {
153-
return true;
169+
return LOGGED_IN_ALREADY;
154170
}
155171

156172
var userByEmail = yield User.findOne({
157173
email: invite.email
158174
}).exec();
159175

160-
if (!userByEmail) return false;
176+
if (!userByEmail) return NO_SUCH_USER;
161177

162178
if (!userByEmail.verifiedEmail) {
163179
// if pending verification => invite token confirms email
@@ -166,7 +182,6 @@ function* loginByInvite(invite) {
166182
});
167183
}
168184

169-
this.locals.wasLoggedIn = true;
170185
yield this.login(userByEmail);
171-
return true;
186+
return LOGIN_SUCCESSFUL;
172187
}

handlers/courses/controller/signup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ exports.get = function*() {
7676

7777

7878
this.locals.formatGroupDate = function(date) {
79-
return moment(date).format('D MMM YY').replace(/[а-я]/, function(letter) {
79+
return moment(date).format('D MMM YYYY').replace(/[а-я]/, function(letter) {
8080
return letter.toUpperCase();
8181
});
8282
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const sendMail = require('mailer').send;
2+
const CourseInvite = require('../models/courseInvite');
3+
const _ = require('lodash');
4+
const log = require('log')();
5+
const sendInvite = require('./sendInvite');
6+
const CourseGroup = require('../models/courseGroup');
7+
const User = require('users').User;
8+
9+
/**
10+
* create invites for the order
11+
* except those that already exist
12+
* @param order
13+
*/
14+
module.exports = function*(order) {
15+
16+
var emails = order.data.emails;
17+
18+
// get existing invites, so that we won't recreate them
19+
var existingInvites = yield CourseInvite.find({ order: order._id }).exec();
20+
var existingInviteByEmails = _.indexBy(existingInvites, 'email');
21+
22+
log.debug("existing invites", existingInviteByEmails);
23+
24+
// get existing participants, they don't need invites
25+
var group = yield CourseGroup.findById(order.data.group).exec();
26+
yield CourseGroup.populate(group, {path: 'participants.user'});
27+
var participantsByEmail = _.indexBy(_.pluck(group.participants, 'user'), 'email');
28+
29+
var invites = [];
30+
for (var i = 0; i < emails.length; i++) {
31+
var email = emails[i];
32+
if (participantsByEmail[email]) continue; // in group already
33+
if (existingInviteByEmails[email]) continue; // invite exists already
34+
35+
log.debug("create invite for email", email);
36+
37+
var invite = new CourseInvite({
38+
order: order._id,
39+
group: group._id,
40+
// max(now + 7 days, course start + 7 days)
41+
validUntil: new Date( Math.max(Date.now(), group.dateStart) + 7 * 24 * 86400 * 1e3),
42+
email: email
43+
});
44+
invites.push(invite);
45+
46+
yield invite.persist();
47+
48+
// not only send invite, but enable the tab so that the user can manually accept it
49+
yield User.update({
50+
email: email
51+
}, {
52+
$addToSet: {profileTabsEnabled: 'courses'}
53+
});
54+
55+
}
56+
57+
return invites;
58+
};

handlers/courses/lib/onPaid.js

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
const Order = require('payments').Order;
2+
const assert = require('assert');
23
const path = require('path');
34
const log = require('log')();
45
const config = require('config');
56
const sendMail = require('mailer').send;
67
const CourseInvite = require('../models/courseInvite');
78
const CourseGroup = require('../models/courseGroup');
8-
const sendOrderInvites = require('./sendOrderInvites');
9+
const createOrderInvites = require('./createOrderInvites');
910
const xmppClient = require('xmppClient');
1011
const VideoKey = require('videoKey').VideoKey;
12+
const sendInvite = require('./sendInvite');
1113

1214
// not a middleware
1315
// can be called from CRON
@@ -25,37 +27,48 @@ module.exports = function* (order) {
2527
// is there anyone except the user?
2628
var orderHasParticipantsExceptUser = emails.length > 1 || emails[0] != order.user.email;
2729

30+
31+
log.debug("orderUserIsParticipant:", orderUserIsParticipant, "orderHasParticipantsExceptUser:", orderHasParticipantsExceptUser);
32+
33+
var invites = yield* createOrderInvites(order);
34+
35+
var orderUserInvite;
36+
// send current user's invite in payment confirmation letter
37+
if (orderUserIsParticipant) {
38+
// probably generated above, but maybe(?) not, ensure we get it anyway
39+
orderUserInvite = yield CourseInvite.findOne({email: order.user.email}).exec();
40+
assert(orderUserInvite);
41+
invites = invites.filter(function(invite) {
42+
return invite.email != order.user.email;
43+
});
44+
}
45+
46+
yield group.persist();
47+
2848
yield sendMail({
2949
templatePath: path.join(__dirname, '..', 'templates', 'successEmail'),
3050
from: 'orders',
3151
to: order.email,
3252
orderNumber: order.number,
3353
subject: "Подтверждение оплаты за курс, заказ " + order.number,
54+
orderUserInviteLink: config.server.siteHost + '/courses/invite/' + orderUserInvite.token,
3455
orderUserIsParticipant: orderUserIsParticipant,
3556
orderHasOtherParticipants: orderHasParticipantsExceptUser
3657
});
3758

59+
// send invites in parallel, for speed
60+
yield invites.map(function(invite) {
61+
return sendInvite(invite);
62+
});
3863

39-
if (orderUserIsParticipant) {
40-
group.participants.push({
41-
user: order.user._id,
42-
courseName: order.data.contactName
43-
});
44-
group.decreaseParticipantsLimit();
45-
order.user.profileTabsEnabled.addToSet('courses');
46-
yield order.user.persist();
47-
}
48-
yield group.persist();
49-
50-
yield* sendOrderInvites(order);
51-
64+
/*
5265
yield CourseGroup.populate(group,[{path: 'participants.user'}, {path: 'course'}]);
5366
5467
yield* grantXmppChatMemberships(group);
5568
5669
if (group.course.videoKeyTag) {
5770
yield *grantVideoKeys(group);
58-
}
71+
}*/
5972

6073

6174
order.status = Order.STATUS_SUCCESS;

handlers/courses/lib/sendOrderInvites.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// DEPRECATED
2+
13
const sendMail = require('mailer').send;
24
const CourseInvite = require('../models/courseInvite');
35
const _ = require('lodash');

handlers/courses/models/courseGroup.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,8 @@ var schema = new Schema({
6565
index: true,
6666
required: true
6767
},
68-
courseName: { // how to call this user in-course?
69-
type: String,
70-
required: true
68+
details: {
69+
// firstname, lastname, other details
7170
},
7271
videoKey: {
7372
type: String

handlers/courses/templates/blocks/register-form-participants.jade

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)