Skip to content

Commit 87aa2a7

Browse files
committed
nodejs screencast now opens in fit-width-height-modal, sends stats, more work on courses
1 parent 29b1e58 commit 87aa2a7

20 files changed

Lines changed: 310 additions & 154 deletions

File tree

client/head/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ require('./trackLinks');
2727
// must use CommonsChunkPlugin
2828
// to ensure that other modules use exactly this (initialized) client/notify
2929
require('client/notification').init();
30+

course

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/js/course

handlers/courses/controller/coursesByUser.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,37 +31,51 @@ exports.get = function*(next) {
3131
this.body = [];
3232

3333
for (let i = 0; i < invites.length; i++) {
34-
let group = formatGroup(invites[i].group);
35-
group.links = [{
34+
35+
let group = invites[i].group;
36+
let groupInfo = formatGroup(group);
37+
groupInfo.links = [{
3638
url: group.getUrl(),
3739
title: 'Описание курса'
3840
}];
39-
group.isParticipant = false;
40-
group.inviteToken = invites[i].token;
41-
this.body.push(group);
41+
groupInfo.inviteUrl = '/courses/invite/' + invites[i].token;
42+
this.body.push(groupInfo);
4243
}
4344

4445
for (let i = 0; i < groups.length; i++) {
45-
let group = formatGroup(groups[i]);
46-
group.links = [{
46+
let group = groups[i];
47+
let groupInfo = formatGroup(group);
48+
groupInfo.links = [{
4749
url: group.getUrl(),
4850
title: 'Описание курса'
4951
}, {
5052
url: `/courses/groups/${group.slug}/info`,
5153
title: 'Инструкции по настройке окружения'
5254
}];
53-
//if ()
54-
group.isParticipant = true;
55-
this.body.push(group);
55+
56+
var materials = yield groups[i].readMaterials();
57+
if (materials.length) {
58+
groupInfo.links.push({
59+
url: `/courses/groups/${group.slug}/download`,
60+
title: 'Материалы для обучения'
61+
});
62+
}
63+
this.body.push(groupInfo);
5664
}
5765

66+
for (var i = 0; i < this.body.length; i++) {
67+
var groupInfo = this.body[i];
68+
groupInfo.status = groupInfo.inviteToken ? 'invite' :
69+
(groupInfo.dateStart > new Date()) ? 'accepted' :
70+
(groupInfo.dateEnd > new Date()) ? 'started' : 'ended';
71+
}
72+
73+
5874
};
5975

6076
function formatGroup(group) {
6177
return {
6278
title: group.title,
63-
groupUrl: group.getUrl(),
64-
groupSlug: group.slug,
6579
dateStart: group.dateStart,
6680
dateEnd: group.dateEnd,
6781
timeDesc: group.timeDesc

handlers/courses/controller/invite.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,14 @@ function* acceptParticipant(invite) {
134134
user: this.user._id,
135135
courseName: this.request.body.courseName || this.user.displayName
136136
});
137+
138+
this.user.profileTabsEnabled.addToSet('courses');
139+
yield this.user.persist();
140+
137141
yield invite.accept();
142+
143+
invite.group.decreaseParticipantsLimit();
144+
138145
yield invite.group.persist();
139146
this.redirect('/courses/invite/' + invite.token);
140147
}

handlers/courses/lib/onPaid.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,9 @@ module.exports = function* (order) {
4141
user: order.user._id,
4242
courseName: order.data.contactName
4343
});
44-
group.participantsLimit--;
45-
}
46-
if (group.participantsLimit < 0) group.participantsLimit = 0;
47-
if (group.participantsLimit === 0) {
48-
group.isOpenForSignup = false; // we're full!
44+
group.decreaseParticipantsLimit();
45+
order.user.profileTabsEnabled.addToSet('courses');
46+
yield order.user.persist();
4947
}
5048
yield group.persist();
5149

handlers/courses/models/courseGroup.js

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
var mongoose = require('mongoose');
22
var Schema = mongoose.Schema;
3+
var config = require('config');
4+
var fs = require('mz/fs');
5+
var path = require('path');
6+
var log = require('log')();
37

48
// the schema follows http://openexchangerates.org/api/latest.json response
59
var schema = new Schema({
@@ -61,17 +65,18 @@ var schema = new Schema({
6165
index: true,
6266
required: true
6367
},
64-
courseName: {
68+
courseName: { // how to call this user in-course?
6569
type: String,
6670
required: true
6771
},
6872
videoKey: {
6973
type: String
70-
// not required
74+
// there may be groups without video & keys
7175
}
7276
}],
7377

74-
// room jid, gotowebinar id
78+
// room jid AND gotowebinar id
79+
// an offline group may not have this
7580
webinarId: {
7681
type: String
7782
},
@@ -101,6 +106,36 @@ schema.methods.getUrl = function() {
101106
return '/courses/groups/' + this.slug;
102107
};
103108

109+
schema.methods.readMaterials = function*() {
110+
var groupDir = path.join(config.courseRoot, this.slug);
111+
112+
113+
try {
114+
var files = yield fs.readdir(groupDir);
115+
return files.map(function(file) {
116+
if (file[0] == '.') return null;
117+
return {
118+
path: path.join(groupDir, file),
119+
url: `/courses/groups/${this.slug}/download/${file}`,
120+
title: file
121+
};
122+
}).filter(Boolean);
123+
} catch (e) {
124+
log.error("Group dir must be a directory", groupDir);
125+
126+
return [];
127+
}
128+
129+
};
130+
131+
schema.methods.decreaseParticipantsLimit = function(count) {
132+
count = count === undefined ? 1 : count;
133+
this.participantsLimit -= count;
134+
if (this.participantsLimit < 0) this.participantsLimit = 0;
135+
if (this.participantsLimit === 0) {
136+
this.isOpenForSignup = false; // we're full!
137+
}
138+
};
104139

105140
module.exports = mongoose.model('CourseGroup', schema);
106141

handlers/courses/router.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ router.get('/groups/:group/info', require('./controller/groupInfo').get);
1313
router.all('/invite/:inviteToken?', require('./controller/invite').all);
1414

1515
// for profile
16-
router.get('/user/:userById', require('./controller/coursesByUser').get);
16+
router.get('/profile/:userById', require('./controller/coursesByUser').get);

handlers/nodejsScreencast/client/index.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var Modal = require('client/head/modal');
22
var courseForm = require('../templates/course-form.jade');
33
var clientRender = require('client/clientRender');
44
var newsletter = require('newsletter/client');
5+
var gaHitCallback = require('gaHitCallback');
56

67
exports.init = function() {
78
initList();
@@ -60,6 +61,52 @@ function initList() {
6061
'</div>'
6162
);
6263
}
64+
65+
var links = document.querySelectorAll('a[data-video-id]');
66+
for (var i = 0; i < links.length; i++) {
67+
var link = links[i];
68+
link.onclick = function(e) {
69+
e.preventDefault();
70+
var videoId = this.getAttribute('data-video-id');
71+
window.ga('send', 'event', 'nodejs-screencast', 'open', videoId, {
72+
hitCallback: gaHitCallback(function() {
73+
openVideo(videoId);
74+
})
75+
});
76+
};
77+
}
6378
}
6479

80+
function openVideo(videoId) {
81+
// sizes from https://developers.google.com/youtube/iframe_api_reference
82+
var sizeList = [
83+
{width: 0, height: 0}, // mobile screens lower than any player => new window
84+
{width: 640, height: 360 + 30},
85+
{width: 853, height: 480 + 30},
86+
{width: 1280, height: 720 + 30}
87+
];
88+
89+
for(var i=0; i<sizeList.length; i++) {
90+
if (document.documentElement.clientHeight < sizeList[i].height ||
91+
document.documentElement.clientWidth < sizeList[i].width) break;
92+
}
93+
i--;
94+
95+
var width = sizeList[i].width;
96+
var height = sizeList[i].height;
97+
window.ga('send', 'event', 'nodejs-screencast', 'open', videoId + '-' + width + 'x' + height, {
98+
hitCallback: gaHitCallback(function() {
99+
100+
if (i === 0) {
101+
window.location.href = '//www.youtube.com/watch?v=' + videoId;
102+
} else {
103+
var modal = new Modal();
104+
modal.setContent(
105+
`<iframe width="${width}" height="${height}" src="//www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen></iframe>`
106+
);
107+
}
108+
})
109+
});
65110

111+
112+
}

0 commit comments

Comments
 (0)