nodebb-plugin-poll 3.1.8 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config.js +42 -39
- package/lib/hooks.js +93 -111
- package/lib/poll.js +218 -304
- package/lib/scheduler.js +45 -56
- package/lib/sockets.js +160 -249
- package/lib/vote.js +65 -120
- package/library.js +8 -11
- package/package.json +1 -1
- package/plugin.json +0 -7
- package/public/js/poll/creator.js +1 -1
- package/public/js/poll/serializer.js +1 -1
- package/public/scss/style.scss +0 -25
- package/templates/poll/view.tpl +5 -6
package/lib/sockets.js
CHANGED
|
@@ -1,258 +1,169 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const async = require('async');
|
|
4
|
-
|
|
5
3
|
const NodeBB = require('./nodebb');
|
|
6
4
|
|
|
7
5
|
const Config = require('./config');
|
|
8
6
|
const Poll = require('./poll');
|
|
9
7
|
const Vote = require('./vote');
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
data.uid
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
Vote.remove(data, (err) => {
|
|
171
|
-
if (err) {
|
|
172
|
-
return callback(new Error('Error during removing vote'));
|
|
173
|
-
}
|
|
174
|
-
Poll.get(data.pollId, socket.uid, false, (err, pollData) => {
|
|
175
|
-
if (err) {
|
|
176
|
-
console.error(err);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
NodeBB.SocketIndex.server.sockets.emit('event:poll.voteChange', pollData);
|
|
180
|
-
callback();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
Sockets.getOptionDetails = function (socket, data, callback) {
|
|
187
|
-
if (!socket.uid || !data || isNaN(parseInt(data.pollId, 10)) || isNaN(parseInt(data.optionId, 10))) {
|
|
188
|
-
return callback(new Error('Invalid request'));
|
|
189
|
-
}
|
|
190
|
-
async.parallel({
|
|
191
|
-
poll: function (next) {
|
|
192
|
-
Poll.getInfo(data.pollId, next);
|
|
193
|
-
},
|
|
194
|
-
option: function (next) {
|
|
195
|
-
Poll.getOption(data.pollId, data.optionId, true, next);
|
|
196
|
-
},
|
|
197
|
-
}, (err, result) => {
|
|
198
|
-
if (err) {
|
|
199
|
-
return callback(new Error('Something went wrong!'));
|
|
200
|
-
}
|
|
201
|
-
const { poll, option } = result;
|
|
202
|
-
if (!option.votes || !option.votes.length) {
|
|
203
|
-
return callback(null, option);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
NodeBB.User.getUsersFields(option.votes, ['uid', 'username', 'userslug', 'picture'], (err, userData) => {
|
|
207
|
-
if (err) {
|
|
208
|
-
console.error(err);
|
|
209
|
-
return callback(err);
|
|
210
|
-
}
|
|
211
|
-
option.votes = userData;
|
|
212
|
-
NodeBB.Plugins.hooks.fire('filter:poll.getOptionDetails', {
|
|
213
|
-
poll,
|
|
214
|
-
option,
|
|
215
|
-
}, (err, result) => {
|
|
216
|
-
callback(err, result.option);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
Sockets.canCreate = function (socket, data, callback) {
|
|
223
|
-
if (!socket.uid || !data || (isNaN(parseInt(data.cid, 10)) && isNaN(parseInt(data.pid, 10)))) {
|
|
224
|
-
return callback(new Error('Invalid request'));
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (!data.cid) {
|
|
228
|
-
async.waterfall([
|
|
229
|
-
function (next) {
|
|
230
|
-
NodeBB.Posts.getPostField(data.pid, 'tid', next);
|
|
231
|
-
},
|
|
232
|
-
function (tid, next) {
|
|
233
|
-
NodeBB.Topics.getTopicField(tid, 'cid', (err, cid) => {
|
|
234
|
-
if (err) {
|
|
235
|
-
console.error(err);
|
|
236
|
-
return callback(new Error('Invalid request'));
|
|
237
|
-
}
|
|
238
|
-
next(null, cid);
|
|
239
|
-
});
|
|
240
|
-
},
|
|
241
|
-
function (cid) {
|
|
242
|
-
checkPrivs(cid, socket.uid, callback);
|
|
243
|
-
},
|
|
244
|
-
]);
|
|
245
|
-
} else {
|
|
246
|
-
checkPrivs(data.cid, socket.uid, callback);
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
function checkPrivs(cid, socketUid, callback) {
|
|
251
|
-
NodeBB.Privileges.categories.can('poll:create', cid, socketUid, (err, can) => {
|
|
252
|
-
if (err || !can) {
|
|
253
|
-
return callback(new Error('[[poll:error.privilege.create]]'), can);
|
|
254
|
-
}
|
|
255
|
-
return callback(null, can);
|
|
256
|
-
});
|
|
9
|
+
const Sockets = exports;
|
|
10
|
+
|
|
11
|
+
Sockets.getConfig = function (socket, data, callback) {
|
|
12
|
+
callback(null, Config.settings.get());
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Sockets.get = async function (socket, data) {
|
|
16
|
+
const allowAnon = Config.settings.get('toggles.allowAnon');
|
|
17
|
+
|
|
18
|
+
if (!data) {
|
|
19
|
+
throw new Error('Invalid request, request data is not defined');
|
|
20
|
+
}
|
|
21
|
+
if (isNaN(parseInt(data.pollId, 10))) {
|
|
22
|
+
throw new Error('Invalid request, pollId is required');
|
|
23
|
+
}
|
|
24
|
+
if ((!socket.uid && !allowAnon)) {
|
|
25
|
+
throw new Error('Invalid request, anonymous access is not allowed');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const pollId = parseInt(data.pollId, 10);
|
|
29
|
+
const pollData = await Poll.get(pollId, socket.uid, !!socket.uid);
|
|
30
|
+
if (!pollData.info.version) {
|
|
31
|
+
throw new Error('Legacy polls are not supported');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pollData.optionType = parseInt(pollData.settings.maxvotes, 10) > 1 ? 'checkbox' : 'radio';
|
|
35
|
+
return pollData;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
Sockets.vote = async function (socket, data) {
|
|
39
|
+
if (!socket.uid) {
|
|
40
|
+
throw new Error('You need to be logged in to vote');
|
|
41
|
+
}
|
|
42
|
+
if (!data || isNaN(parseInt(data.pollId, 10)) || !data.options || !data.options.length) {
|
|
43
|
+
throw new Error('Invalid vote');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
data.uid = socket.uid;
|
|
47
|
+
|
|
48
|
+
const [canVote, optionsFilter, settings] = await Promise.all([
|
|
49
|
+
Vote.canVote(data.uid, data.pollId),
|
|
50
|
+
Poll.hasOptions(data.pollId, data.options),
|
|
51
|
+
Poll.getSettings(data.pollId),
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
// Filter the options on their existence
|
|
55
|
+
data.options = data.options.filter((el, index) => optionsFilter[index]);
|
|
56
|
+
|
|
57
|
+
// Give an error if there are too many votes
|
|
58
|
+
if (data.options.length > parseInt(settings.maxvotes, 10)) {
|
|
59
|
+
throw new Error(`You can only vote for ${settings.maxvotes} options on this poll.`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!canVote || !data.options.length) {
|
|
63
|
+
throw new Error('Already voted or invalid option');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await Vote.add(data);
|
|
67
|
+
|
|
68
|
+
const pollData = await Poll.get(data.pollId, socket.uid, false);
|
|
69
|
+
NodeBB.SocketIndex.server.sockets.emit('event:poll.voteChange', pollData);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
Sockets.updateVote = async function (socket, data) {
|
|
73
|
+
if (!socket.uid) {
|
|
74
|
+
throw new Error('You need to be logged in to make changes');
|
|
75
|
+
}
|
|
76
|
+
if (!data || isNaN(parseInt(data.pollId, 10)) || !data.options || !data.options.length) {
|
|
77
|
+
throw new Error('Invalid vote');
|
|
78
|
+
}
|
|
79
|
+
data.uid = socket.uid;
|
|
80
|
+
|
|
81
|
+
const [canUpdateVote, optionsFilter, settings] = await Promise.all([
|
|
82
|
+
Vote.canUpdateVote(data.uid, data.pollId),
|
|
83
|
+
Poll.hasOptions(data.pollId, data.options),
|
|
84
|
+
Poll.getSettings(data.pollId),
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
// Filter the options on their existence
|
|
88
|
+
data.options = data.options.filter((el, index) => optionsFilter[index]);
|
|
89
|
+
|
|
90
|
+
// Give an error if there are too many votes
|
|
91
|
+
if (data.options.length > parseInt(settings.maxvotes, 10)) {
|
|
92
|
+
throw new Error(`You can only vote for ${settings.maxvotes} options on this poll.`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!canUpdateVote) {
|
|
96
|
+
throw new Error('Can\'t update vote');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!data.options.length) {
|
|
100
|
+
throw new Error('Invalid option');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await Vote.update(data);
|
|
104
|
+
const pollData = await Poll.get(data.pollId, socket.uid, false);
|
|
105
|
+
NodeBB.SocketIndex.server.sockets.emit('event:poll.voteChange', pollData);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
Sockets.removeVote = async function (socket, data) {
|
|
109
|
+
if (!socket.uid) {
|
|
110
|
+
throw new Error('You need to be logged in to make changes');
|
|
111
|
+
}
|
|
112
|
+
if (!data || isNaN(parseInt(data.pollId, 10))) {
|
|
113
|
+
throw new Error('Invalid request');
|
|
114
|
+
}
|
|
115
|
+
data.uid = socket.uid;
|
|
116
|
+
|
|
117
|
+
const canUpdateVote = await Vote.canUpdateVote(data.uid, data.pollId);
|
|
118
|
+
if (!canUpdateVote) {
|
|
119
|
+
throw new Error('Can\'t remove vote');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await Vote.remove(data);
|
|
123
|
+
const pollData = await Poll.get(data.pollId, socket.uid, false);
|
|
124
|
+
NodeBB.SocketIndex.server.sockets.emit('event:poll.voteChange', pollData);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
Sockets.getOptionDetails = async function (socket, data) {
|
|
128
|
+
if (!socket.uid || !data || isNaN(parseInt(data.pollId, 10)) || isNaN(parseInt(data.optionId, 10))) {
|
|
129
|
+
throw new Error('Invalid request');
|
|
130
|
+
}
|
|
131
|
+
const [poll, option] = await Promise.all([
|
|
132
|
+
Poll.getInfo(data.pollId),
|
|
133
|
+
Poll.getOption(data.pollId, data.optionId, true),
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
if (!option.votes || !option.votes.length) {
|
|
137
|
+
return option;
|
|
138
|
+
}
|
|
139
|
+
const userData = await NodeBB.User.getUsersFields(option.votes, [
|
|
140
|
+
'uid', 'username', 'userslug', 'picture',
|
|
141
|
+
]);
|
|
142
|
+
option.votes = userData;
|
|
143
|
+
|
|
144
|
+
const result = await NodeBB.Plugins.hooks.fire('filter:poll.getOptionDetails', {
|
|
145
|
+
poll,
|
|
146
|
+
option,
|
|
147
|
+
});
|
|
148
|
+
return result.option;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
Sockets.canCreate = async function (socket, data) {
|
|
152
|
+
if (!socket.uid || !data || (isNaN(parseInt(data.cid, 10)) && isNaN(parseInt(data.pid, 10)))) {
|
|
153
|
+
throw new Error('Invalid request');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!data.cid) {
|
|
157
|
+
const tid = await NodeBB.Posts.getPostField(data.pid, 'tid');
|
|
158
|
+
data.cid = await NodeBB.Topics.getTopicField(tid, 'cid');
|
|
159
|
+
}
|
|
160
|
+
return await checkPrivs(data.cid, socket.uid);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
async function checkPrivs(cid, socketUid) {
|
|
164
|
+
const can = await NodeBB.Privileges.categories.can('poll:create', cid, socketUid);
|
|
165
|
+
if (!can) {
|
|
166
|
+
throw new Error('[[poll:error.privilege.create]]');
|
|
257
167
|
}
|
|
258
|
-
|
|
168
|
+
return true;
|
|
169
|
+
}
|
package/lib/vote.js
CHANGED
|
@@ -1,138 +1,83 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const async = require('async');
|
|
4
3
|
const NodeBB = require('./nodebb');
|
|
5
4
|
const Poll = require('./poll');
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
Vote.add = function (voteData, callback) {
|
|
9
|
-
const { pollId } = voteData;
|
|
10
|
-
const { options } = voteData;
|
|
11
|
-
const { uid } = voteData;
|
|
12
|
-
const timestamp = +new Date();
|
|
6
|
+
const Vote = exports;
|
|
13
7
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// Add uid to list of votes
|
|
18
|
-
NodeBB.db.sortedSetAdd(`poll:${pollId}:options:${option}:votes`, timestamp, uid, next);
|
|
19
|
-
}, next);
|
|
20
|
-
},
|
|
21
|
-
function (next) {
|
|
22
|
-
NodeBB.db.sortedSetAdd(`poll:${pollId}:voters`, timestamp, uid, next);
|
|
23
|
-
},
|
|
24
|
-
], (err) => {
|
|
25
|
-
if (err) {
|
|
26
|
-
return callback(err, false);
|
|
27
|
-
}
|
|
8
|
+
Vote.add = async function (voteData) {
|
|
9
|
+
const { pollId, options, uid } = voteData;
|
|
10
|
+
const timestamp = +new Date();
|
|
28
11
|
|
|
29
|
-
|
|
30
|
-
})
|
|
31
|
-
|
|
12
|
+
await Promise.all([
|
|
13
|
+
NodeBB.db.sortedSetAdd(`poll:${pollId}:voters`, timestamp, uid),
|
|
14
|
+
NodeBB.db.sortedSetAddBulk(
|
|
15
|
+
options.map(option => ([`poll:${pollId}:options:${option}:votes`, timestamp, uid]))
|
|
16
|
+
),
|
|
17
|
+
]);
|
|
18
|
+
};
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
Vote.remove = async function (voteData) {
|
|
21
|
+
return await Vote.removeUidVote(voteData.uid, voteData.pollId);
|
|
22
|
+
};
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
Vote.get = async function (voteData) {
|
|
25
|
+
return await Vote.getUidVote(voteData.uid, voteData.pollId);
|
|
26
|
+
};
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
Vote.removeUidVote = async function (uid, pollId) {
|
|
29
|
+
const vote = await Vote.getUidVote(uid, pollId);
|
|
30
|
+
const options = vote.options || [];
|
|
31
|
+
await Promise.all([
|
|
32
|
+
NodeBB.db.sortedSetRemove(`poll:${pollId}:voters`, uid),
|
|
33
|
+
NodeBB.db.sortedSetsRemove(
|
|
34
|
+
options.map(option => `poll:${pollId}:options:${option}:votes`),
|
|
35
|
+
uid,
|
|
36
|
+
),
|
|
37
|
+
]);
|
|
38
|
+
};
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
], (err) => {
|
|
57
|
-
if (err) {
|
|
58
|
-
return callback(err, false);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
callback(null, true);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
40
|
+
Vote.getUidVote = async function (uid, pollId) {
|
|
41
|
+
const options = await Poll.getOptions(pollId, true);
|
|
42
|
+
return {
|
|
43
|
+
pollId: pollId,
|
|
44
|
+
uid: uid,
|
|
45
|
+
options: options
|
|
46
|
+
.filter(option => option.votes.some(_uid => parseInt(_uid, 10) === parseInt(uid, 10)))
|
|
47
|
+
.map(option => option.id),
|
|
64
48
|
};
|
|
49
|
+
};
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
uid: uid,
|
|
71
|
-
options: options
|
|
72
|
-
.filter(option => option.votes.some(_uid => parseInt(_uid, 10) === parseInt(uid, 10)))
|
|
73
|
-
.map(option => option.id),
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
};
|
|
51
|
+
Vote.update = async function (voteData) {
|
|
52
|
+
await Vote.remove(voteData);
|
|
53
|
+
await Vote.add(voteData);
|
|
54
|
+
};
|
|
77
55
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
], callback);
|
|
87
|
-
};
|
|
56
|
+
Vote.canUpdateVote = async function (uid, pollId) {
|
|
57
|
+
const [ended, deleted, updateDisabled] = await Promise.all([
|
|
58
|
+
Poll.hasEnded(pollId),
|
|
59
|
+
Poll.isDeleted(pollId),
|
|
60
|
+
Poll.doesDisallowVoteUpdate(pollId),
|
|
61
|
+
]);
|
|
62
|
+
return !ended && !deleted && !updateDisabled;
|
|
63
|
+
};
|
|
88
64
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Poll.isDeleted(pollId, next);
|
|
98
|
-
},
|
|
99
|
-
function (next) {
|
|
100
|
-
// voteUpdate?
|
|
101
|
-
Poll.doesDisallowVoteUpdate(pollId, next);
|
|
102
|
-
},
|
|
103
|
-
], (err, result) => {
|
|
104
|
-
callback(err, result.indexOf(true) === -1);
|
|
105
|
-
});
|
|
106
|
-
};
|
|
65
|
+
Vote.canVote = async function (uid, pollId) {
|
|
66
|
+
const [ended, deleted, hasVoted] = await Promise.all([
|
|
67
|
+
Poll.hasEnded(pollId),
|
|
68
|
+
Poll.isDeleted(pollId),
|
|
69
|
+
Vote.hasUidVoted(uid, pollId),
|
|
70
|
+
]);
|
|
71
|
+
return !ended && !deleted && !hasVoted;
|
|
72
|
+
};
|
|
107
73
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
Poll.hasEnded(pollId, next);
|
|
113
|
-
},
|
|
114
|
-
function (next) {
|
|
115
|
-
// Deleted?
|
|
116
|
-
Poll.isDeleted(pollId, next);
|
|
117
|
-
},
|
|
118
|
-
function (next) {
|
|
119
|
-
// Already voted?
|
|
120
|
-
Vote.hasUidVoted(uid, pollId, next);
|
|
121
|
-
},
|
|
122
|
-
], (err, result) => {
|
|
123
|
-
callback(err, result.indexOf(true) === -1);
|
|
124
|
-
});
|
|
125
|
-
};
|
|
74
|
+
Vote.hasUidVoted = async function (uid, pollId) {
|
|
75
|
+
const score = await NodeBB.db.sortedSetScore(`poll:${pollId}:voters`, uid);
|
|
76
|
+
return !!score;
|
|
77
|
+
};
|
|
126
78
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
};
|
|
79
|
+
Vote.hasUidVotedOnOption = async function (uid, pollId, option) {
|
|
80
|
+
const score = await NodeBB.db.sortedSetScore(`poll:${pollId}:options:${option}:votes`, uid);
|
|
81
|
+
return !!score;
|
|
82
|
+
};
|
|
132
83
|
|
|
133
|
-
Vote.hasUidVotedOnOption = function (uid, pollId, option, callback) {
|
|
134
|
-
NodeBB.db.sortedSetScore(`poll:${pollId}:options:${option}:votes`, uid, (err, score) => {
|
|
135
|
-
callback(err, !!score);
|
|
136
|
-
});
|
|
137
|
-
};
|
|
138
|
-
}(exports));
|