nodebb-plugin-poll 3.2.9 → 4.0.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/README.md CHANGED
@@ -1,27 +1,10 @@
1
1
  # NodeBB Poll plugin
2
2
 
3
- This NodeBB plugin will allow you to add polls to the first post of a topic with the following markup:
3
+ This NodeBB plugin will allow you to add polls to posts.
4
4
 
5
- [poll <settings>]
6
- - Poll option
7
- - Another option
8
- [/poll]
9
-
10
- Currently supported settings:
11
-
12
- maxvotes="1" //Max number of votes per user. If larger than 1, a multiple choice poll will be created
13
- disallowVoteUpdate="0" //if set, users won't be able to update/remove their vote
14
- allowAnonVoting="0" // if set to 1, users will be able to vote anonymously
15
- title="Poll title" //Poll title
16
-
17
- There's also a helpful modal available that will allow you to easily create a poll:
5
+ There's helpful modal available that will allow you to easily create a poll:
18
6
  ![](https://i.imgur.com/2fPnWLb.png)
19
7
 
20
- ## Todo
21
-
22
- - Add the ability to edit a poll
23
- - A lot more...
24
-
25
8
  If you're willing to help, please make any improvements you want and submit a PR.
26
9
 
27
10
  ## Installation
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "poll": "Poll",
3
+ "poll-id-x": "Poll ID: %1",
3
4
  "toggles": "Toggles",
4
5
  "allow_guests": "Allow guests to view poll results",
5
6
  "limits": "Limits",
@@ -13,8 +14,12 @@
13
14
  "settings": "Settings",
14
15
  "save": "Save",
15
16
  "reset": "Reset",
16
-
17
+ "manage-polls": "Manage polls",
18
+ "add-poll": "Add poll",
19
+ "no-polls": "No polls found",
20
+ "confirm-remove": "Are you sure you want to remove this poll?",
17
21
  "creator_title": "Create a poll",
22
+ "edit-a-poll-title": "Edit a poll",
18
23
  "poll_title": "Poll Title",
19
24
  "poll_title_placeholder": "Enter poll title",
20
25
  "options_title": "Options",
@@ -24,11 +29,12 @@
24
29
  "auto_end_placeholder": "Click to enter date and time",
25
30
  "auto_end_help": "Leaving this empty will never end the poll.",
26
31
 
27
- "error.max_options": "You can only create %d options.",
32
+ "error.max_options": "You can only create %1 options.",
28
33
  "error.no_options": "Create at least one option.",
29
34
  "error.valid_date": "Please enter a valid date.",
30
35
 
31
36
  "error.not_main": "Can only add poll in main post.",
37
+ "error.invalid-post": "Invalid post.",
32
38
  "error.privilege.create": "You're not allowed to create a poll",
33
39
  "error.anon-voting-not-allowed": "This poll does not allow voting anonymously",
34
40
 
@@ -45,9 +51,11 @@
45
51
  "voting_update_disallowed_message": "You have already voted and changing your vote is not allowed for this poll",
46
52
  "vote_is_final": "Changing your vote is not allowed for this poll so your vote is final",
47
53
 
48
- "vote_count": "users voted for this option",
54
+ "x-users-voted-for-this-option": "%1 user(s) voted for this option",
49
55
  "votes": "votes",
50
56
  "x-votes": "%1 votes",
51
57
  "total-votes-x": "Total Votes: %1",
52
- "admin.create-poll": "Create Poll"
58
+ "admin.create-poll": "Create Poll",
59
+ "created-time": "Created",
60
+ "end-time": "End"
53
61
  }
@@ -1,39 +1,47 @@
1
- {
2
- "poll": "Sondage",
3
- "toggles": "Activateurs",
4
- "allow_guests": "Permettre aux invités de voir les résultats du sondage",
5
- "limits": "Limites",
6
- "max_options": "Nombre maximum d'options par sondage",
7
- "defaults": "Défauts",
8
- "default_title": "Titre par défaut",
9
- "max_votes": "Nombre maximum de votes par utilisateur",
10
- "info_choices": "Un nombre supérieur à 1 permet d'activer les questionnaires à choix multiple.",
11
- "settings": "Paramètres",
12
- "save": "Sauver",
13
- "reset": "Remettre à zéro",
14
-
15
- "creator_title": "Créer un sondage",
16
- "poll_title": "Titre du sondage",
17
- "poll_title_placeholder": "Entrez le titre du sondage",
18
- "options_title": "Options",
19
- "options_add": "Add option",
20
- "max_votes_placeholder": "Entrez le montant",
21
- "auto_end_title": "Terminer le sondage automatiquement",
22
- "auto_end_placeholder": "Cliquez pour entrer la date et l'heure",
23
- "auto_end_help": "Laissez ce champ vide pour que le sondage ne s'arrête jamais",
24
-
25
- "error.max_options": "You can only create %d options.",
26
- "error.no_options": "Create at least one option.",
27
- "error.valid_date": "Please enter a valid date.",
28
-
29
- "error.privilege.create": "You're not allowed to create a poll",
30
-
31
- "vote": "Voter",
32
- "to_voting": "To Voting",
33
- "to_results": "Résultats",
34
- "voting_unavailable_title": "Voting unavailable",
35
- "voting_unavailable_message": "This poll has ended or has been marked as deleted. You can still view the results.",
36
-
37
- "vote_count": "utilisateurs ont voté en faveur de ce choix",
38
- "votes": "votes"
1
+ {
2
+ "poll": "Sondage",
3
+ "toggles": "Activateurs",
4
+ "allow_guests": "Permettre aux invités de voir les résultats du sondage",
5
+ "limits": "Limites",
6
+ "max_options": "Nombre maximum d'options par sondage",
7
+ "defaults": "Valeurs par défaut",
8
+ "default_title": "Titre du sondage par défaut",
9
+ "max_votes": "Nombre maximum de votes par utilisateur",
10
+ "disallow_vote_update": "Ne pas autoriser les utilisateurs à modifier leur vote",
11
+ "allow_anon_voting": "Autoriser le vote anonyme",
12
+ "info_choices": "Un nombre supérieur à 1 permet d'activer les sondages à choix multiple.",
13
+ "settings": "Paramètres",
14
+ "save": "Enregistrer",
15
+ "reset": "Réinitialiser",
16
+ "creator_title": "Créer un sondage",
17
+ "poll_title": "Titre du sondage",
18
+ "poll_title_placeholder": "Entrez le titre du sondage",
19
+ "options_title": "Options",
20
+ "options_add": "Ajouter une option",
21
+ "max_votes_placeholder": "Entrez le nombre",
22
+ "auto_end_title": "Terminer le sondage automatiquement",
23
+ "auto_end_placeholder": "Cliquez pour entrer la date et l'heure",
24
+ "auto_end_help": "Laisser ce champ vide pour que le sondage ne se termine jamais.",
25
+ "error.max_options": "Vous ne pouvez créer que %d options.",
26
+ "error.no_options": "Créez au moins une option.",
27
+ "error.valid_date": "Veuillez entrer une date valide.",
28
+ "error.not_main": "Le sondage ne peut être ajouté que dans le post principal.",
29
+ "error.privilege.create": "Vous n'êtes pas autorisé à créer un sondage.",
30
+ "error.anon-voting-not-allowed": "Ce sondage n'autorise pas le vote anonyme.",
31
+ "warning.redactor": "Vous utilisez Redactor. Ne modifiez pas le balisage manuellement. Rouvrez plutôt le créateur de sondage.",
32
+ "vote": "Voter",
33
+ "vote_anonymously": "Voter anonymement",
34
+ "update_vote": "Modifier le vote",
35
+ "remove_vote": "Supprimer le vote",
36
+ "to_voting": "Aller au vote",
37
+ "to_results": "Voir les résultats",
38
+ "voting_unavailable_title": "Vote indisponible",
39
+ "voting_unavailable_message": "Ce sondage est terminé ou a été marqué comme supprimé. Vous pouvez toujours consulter les résultats.",
40
+ "voting_update_disallowed_message": "Vous avez déjà voté et la modification de votre vote n'est pas autorisée pour ce sondage.",
41
+ "vote_is_final": "La modification de votre vote n'est pas autorisée pour ce sondage, votre vote est donc définitif.",
42
+ "vote_count": "utilisateurs ont voté pour cette option",
43
+ "votes": "votes",
44
+ "x-votes": "%1 votes",
45
+ "total-votes-x": "Total des votes : %1",
46
+ "admin.create-poll": "Créer un sondage"
39
47
  }
package/lib/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const NodeBB = require('./nodebb');
3
+ const meta = require.main.require('./src/meta');
4
4
 
5
5
  const packageInfo = require('../package.json');
6
6
  const pluginInfo = require('../plugin.json');
@@ -18,35 +18,19 @@ Config.plugin = {
18
18
  };
19
19
 
20
20
  Config.defaults = {
21
- toggles: {
22
- allowAnon: false,
23
- },
24
- limits: {
25
- maxOptions: 10,
26
- },
27
- defaults: {
28
- title: 'Poll',
29
- maxvotes: 1,
30
- disallowVoteUpdate: 0,
31
- allowAnonVoting: 0,
32
- end: 0,
33
- },
21
+ defaultTitle: 'Poll',
22
+ maxOptions: 10,
23
+ maximumVotesPerUser: 1,
24
+ allowGuestsToViewResults: 0,
25
+ disallowVoteUpdate: 0,
26
+ allowAnonVoting: 0,
34
27
  };
35
28
 
36
- Config.settings = {};
37
-
38
- Config.init = function () {
39
- return new Promise((resolve) => {
40
- Config.settings = new NodeBB.Settings(Config.plugin.id, Config.plugin.version, Config.defaults, resolve);
41
- });
42
- };
43
-
44
- Config.adminSockets = {
45
- sync: function () {
46
- Config.settings.sync();
47
- },
48
- getDefaults: function (socket, data, callback) {
49
- callback(null, Config.settings.createDefaultWrapper());
50
- },
29
+ Config.getSettings = async function () {
30
+ const settings = await meta.settings.get(Config.plugin.id);
31
+ return {
32
+ ...Config.defaults,
33
+ ...settings,
34
+ };
51
35
  };
52
36
 
package/lib/hooks.js CHANGED
@@ -1,117 +1,131 @@
1
1
  'use strict';
2
2
 
3
- const NodeBB = require('./nodebb');
3
+ const db = require.main.require('./src/database');
4
+ const topics = require.main.require('./src/topics');
5
+ const posts = require.main.require('./src/posts');
6
+ const privileges = require.main.require('./src/privileges');
7
+
4
8
 
5
9
  const Config = require('./config');
6
10
  const Poll = require('./poll');
7
- const Serializer = require('./serializer');
8
11
 
9
12
  const Hooks = exports;
10
13
 
11
14
  Hooks.filter = {};
12
15
  Hooks.action = {};
13
16
 
14
- Hooks.filter.parseRaw = function (raw) {
15
- return Serializer.removeMarkup(raw, '[Poll]');
17
+ Hooks.filter.configGet = async function (config) {
18
+ config.poll = await Config.getSettings();
19
+ return config;
16
20
  };
17
21
 
18
- Hooks.filter.postCreate = async function (obj) {
19
- if (Serializer.hasMarkup(obj.post.content) && obj.data.isMain) {
20
- return await savePoll(obj);
21
- }
22
- return obj;
22
+ Hooks.filter.registerFormatting = function (payload) {
23
+ payload.options.push({
24
+ name: 'poll',
25
+ className: `fa ${Config.plugin.icon}`,
26
+ title: '[[poll:manage-polls]]',
27
+ badge: true,
28
+ });
29
+ return payload;
23
30
  };
24
31
 
25
- Hooks.filter.postEdit = async function (obj) {
26
- const { tid, pollId } = await NodeBB.Posts.getPostFields(obj.data.pid, ['tid', 'pollId']);
27
- if (pollId || !Serializer.hasMarkup(obj.post.content)) {
28
- return obj;
32
+ Hooks.filter.composerPush = async function (hookData) {
33
+ // used for editing, add the polls so they are avable in composer
34
+ if (hookData.pid) {
35
+ const callerUid = hookData?.caller?.uid || 0;
36
+ const pollIds = await Poll.getPollIdsByPid(hookData.pid);
37
+ const polls = await Promise.all(pollIds.map(pollId => Poll.get(pollId, callerUid)));
38
+ hookData.polls = polls.map(p => p && p.info).filter(Boolean);
29
39
  }
40
+ return hookData;
41
+ };
30
42
 
31
- const result = await NodeBB.Topics.getTopicFields(tid, ['mainPid', 'cid']);
32
- if (parseInt(result.mainPid, 10) !== parseInt(obj.data.pid, 10)) {
33
- return obj;
43
+ Hooks.filter.postCreate = async function (hookData) {
44
+ // post is going to be saved to db, data is what is submitted by user
45
+ const { post, data } = hookData;
46
+ if (Array.isArray(data?.polls) && data.polls.length) {
47
+ const savedPolls = await Poll.add(post, data.polls);
48
+ if (savedPolls.length) {
49
+ post.pollIds = JSON.stringify(savedPolls.map(p => String(p.pollId)));
50
+ }
34
51
  }
52
+ return hookData;
53
+ };
35
54
 
36
- await canCreate(result.cid, obj.post.editor);
55
+ Hooks.filter.postGetFields = async function (hookData) {
56
+ if (!hookData.fields.includes('pollIds')) {
57
+ // when a new post is created it doesnt send pollIds to the client
58
+ // force load pollIds so it is available when the client needs to load the poll data
59
+ const postData = await db.getObjectsFields(hookData.pids.map(pid => `post:${pid}`), ['pollIds']);
60
+ hookData.posts.forEach((post, index) => {
61
+ if (post) {
62
+ post.pollIds = postData[index].pollIds || '';
63
+ }
64
+ });
65
+ }
66
+ return hookData;
67
+ };
37
68
 
38
- const postData = await savePoll({
39
- ...obj.post,
40
- uid: obj.data.uid,
41
- pid: obj.data.pid,
42
- tid: tid,
43
- });
44
- delete postData.uid;
45
- delete postData.pid;
46
- delete postData.tid;
47
- obj.post = postData;
69
+ Hooks.filter.postEdit = async function (hookData) {
70
+ const currentPollIds = await Poll.getPollIdsByPid(hookData.data.pid);
71
+ const toAdd = hookData.data.polls.filter(p => !currentPollIds.includes(String(p.pollId)));
48
72
 
49
- if (!postData.pollId) {
50
- return obj;
73
+ if (toAdd.length) {
74
+ const cid = await posts.getCidByPid(hookData.data.pid, 'cid');
75
+ await canCreate(cid, hookData.data.uid);
51
76
  }
52
77
 
53
- // NodeBB only updates the edited, editor and content fields, so we add the pollId field manually.
54
- await NodeBB.Posts.setPostField(obj.data.pid, 'pollId', postData.pollId);
55
- return obj;
78
+ await Poll.edit({
79
+ ...hookData.post,
80
+ uid: hookData.data.uid,
81
+ pid: hookData.data.pid,
82
+ }, hookData.data.polls);
83
+ hookData.post.pollIds = JSON.stringify(hookData.data.polls.map(p => String(p.pollId)));
84
+ return hookData;
56
85
  };
57
86
 
58
87
  Hooks.filter.topicPost = async function (data) {
59
- if (Serializer.hasMarkup(data.content)) {
88
+ if (Array.isArray(data.polls) && data.polls.length) {
60
89
  await canCreate(data.cid, data.uid);
61
- return data;
62
90
  }
63
91
  return data;
64
92
  };
65
93
 
66
- Hooks.action.postDelete = async function (data) {
67
- const pollId = await Poll.getPollIdByPid(data.post.pid);
68
- if (pollId) {
69
- await Poll.delete(pollId);
94
+ Hooks.filter.topicReply = async function (data) {
95
+ if (Array.isArray(data.polls) && data.polls.length) {
96
+ const cid = await topics.getTopicField(data.tid, 'cid');
97
+ await canCreate(cid, data.uid);
70
98
  }
99
+ return data;
71
100
  };
72
101
 
73
- Hooks.action.postRestore = async function (data) {
74
- const pollId = await Poll.getPollIdByPid(data.post.pid);
75
- if (pollId) {
76
- await Poll.restore(pollId);
102
+ Hooks.action.postDelete = async function (data) {
103
+ const pollIds = await Poll.getPollIdsByPid(data.post.pid);
104
+ if (pollIds.length) {
105
+ await Poll.delete(pollIds);
77
106
  }
78
107
  };
79
108
 
80
- Hooks.action.topicDelete = async function (data) {
81
- const pollId = await Poll.getPollIdByTid(data.topic.tid);
82
- if (pollId) {
83
- Poll.delete(pollId);
109
+ Hooks.action.postRestore = async function (data) {
110
+ const pollIds = await Poll.getPollIdsByPid(data.post.pid);
111
+ if (pollIds.length) {
112
+ await Poll.restore(pollIds);
84
113
  }
85
114
  };
86
115
 
87
- Hooks.action.topicRestore = async function (data) {
88
- const pollId = await Poll.getPollIdByTid(data.topic.tid);
89
- if (pollId) {
90
- Poll.restore(pollId);
116
+ Hooks.action.postsPurge = async function (data) {
117
+ const { posts } = data;
118
+ const pollIds = await Poll.getPollIdsByPids(posts.map(p => p.pid));
119
+ const toDelete = pollIds.flat();
120
+ if (toDelete.length) {
121
+ await Poll.deletePolls(toDelete);
91
122
  }
92
123
  };
93
124
 
94
125
  async function canCreate(cid, uid) {
95
- const can = await NodeBB.Privileges.categories.can('poll:create', cid, uid);
126
+ const can = await privileges.categories.can('poll:create', cid, uid);
96
127
  if (!can) {
97
128
  throw new Error('[[poll:error.privilege.create]]');
98
129
  }
99
130
  }
100
131
 
101
- // called from filter:post.edit and filter:post.create
102
- async function savePoll(obj) {
103
- const postobj = obj.post ? obj.post : obj;
104
- const pollData = Serializer.serialize(postobj.content, Config.settings.get());
105
-
106
- if (!pollData || !pollData.options.length) {
107
- return obj;
108
- }
109
-
110
- const pollId = await Poll.add(pollData, postobj);
111
-
112
- postobj.pollId = pollId;
113
- postobj.content = Serializer.removeMarkup(postobj.content);
114
-
115
- return obj;
116
- }
117
-
package/lib/nodebb.js CHANGED
@@ -10,7 +10,6 @@ module.exports = {
10
10
  Privileges: require.main.require('./src/privileges'),
11
11
  Plugins: require.main.require('./src/plugins'),
12
12
  PluginSockets: require.main.require('./src/socket.io/plugins'),
13
- AdminSockets: require.main.require('./src/socket.io/admin').plugins,
14
13
  SocketIndex: require.main.require('./src/socket.io/index'),
15
14
  Translator: require.main.require('./src/translator'),
16
15
  winston: require.main.require('winston'),