mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
feat(visitors): Adds support for visitors voting in polls.
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
import { IStateful } from '../base/app/types';
|
import { IStateful } from '../base/app/types';
|
||||||
import { toState } from '../base/redux/functions';
|
import { toState } from '../base/redux/functions';
|
||||||
import { iAmVisitor } from '../visitors/functions';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells whether or not the notifications should be displayed within
|
* Tells whether or not the notifications should be displayed within
|
||||||
@@ -36,5 +34,5 @@ export function arePollsDisabled(stateful: IStateful) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state['features/base/config']?.disablePolls || iAmVisitor(state);
|
return state['features/base/config']?.disablePolls;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { IReduxState } from '../app/types';
|
import { IReduxState } from '../app/types';
|
||||||
import { MEET_FEATURES } from '../base/jwt/constants';
|
import { MEET_FEATURES } from '../base/jwt/constants';
|
||||||
import { isJwtFeatureEnabled } from '../base/jwt/functions';
|
import { isJwtFeatureEnabled } from '../base/jwt/functions';
|
||||||
|
import { iAmVisitor } from '../visitors/functions';
|
||||||
|
|
||||||
import { IAnswerData } from './types';
|
import { IAnswerData } from './types';
|
||||||
|
|
||||||
@@ -72,6 +73,10 @@ export function hasIdenticalAnswers(currentAnswers: Array<IAnswerData>): boolean
|
|||||||
* @returns {boolean} - Returns true if the participant is not allowed to create polls.
|
* @returns {boolean} - Returns true if the participant is not allowed to create polls.
|
||||||
*/
|
*/
|
||||||
export function isCreatePollDisabled(state: IReduxState) {
|
export function isCreatePollDisabled(state: IReduxState) {
|
||||||
|
if (iAmVisitor(state)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const { pollCreationRequiresPermission } = state['features/dynamic-branding'];
|
const { pollCreationRequiresPermission } = state['features/dynamic-branding'];
|
||||||
|
|
||||||
if (!pollCreationRequiresPermission) {
|
if (!pollCreationRequiresPermission) {
|
||||||
|
|||||||
@@ -55,25 +55,35 @@ Setting up configuration for the main prosody is a manual process:
|
|||||||
s2sout_override = {
|
s2sout_override = {
|
||||||
["conference.v1.meet.jitsi"] = "tcp://127.0.0.1:52691";
|
["conference.v1.meet.jitsi"] = "tcp://127.0.0.1:52691";
|
||||||
["v1.meet.jitsi"] = "tcp://127.0.0.1:52691"; -- needed for v1.meet.jitsi->visitors.jitmeet.example.com
|
["v1.meet.jitsi"] = "tcp://127.0.0.1:52691"; -- needed for v1.meet.jitsi->visitors.jitmeet.example.com
|
||||||
|
["polls.v1.meet.jitsi"] = "tcp://127.0.0.1:52691";
|
||||||
["conference.v2.meet.jitsi"] = "tcp://127.0.0.1:52692";
|
["conference.v2.meet.jitsi"] = "tcp://127.0.0.1:52692";
|
||||||
["v2.meet.jitsi"] = "tcp://127.0.0.1:52692";
|
["v2.meet.jitsi"] = "tcp://127.0.0.1:52692";
|
||||||
|
["polls.v2.meet.jitsi"] = "tcp://127.0.0.1:52692";
|
||||||
["conference.v3.meet.jitsi"] = "tcp://127.0.0.1:52693";
|
["conference.v3.meet.jitsi"] = "tcp://127.0.0.1:52693";
|
||||||
["v3.meet.jitsi"] = "tcp://127.0.0.1:52693";
|
["v3.meet.jitsi"] = "tcp://127.0.0.1:52693";
|
||||||
|
["polls.v3.meet.jitsi"] = "tcp://127.0.0.1:52693";
|
||||||
["conference.v4.meet.jitsi"] = "tcp://127.0.0.1:52694";
|
["conference.v4.meet.jitsi"] = "tcp://127.0.0.1:52694";
|
||||||
["v4.meet.jitsi"] = "tcp://127.0.0.1:52694";
|
["v4.meet.jitsi"] = "tcp://127.0.0.1:52694";
|
||||||
|
["polls.v4.meet.jitsi"] = "tcp://127.0.0.1:52694";
|
||||||
["conference.v5.meet.jitsi"] = "tcp://127.0.0.1:52695";
|
["conference.v5.meet.jitsi"] = "tcp://127.0.0.1:52695";
|
||||||
["v5.meet.jitsi"] = "tcp://127.0.0.1:52695";
|
["v5.meet.jitsi"] = "tcp://127.0.0.1:52695";
|
||||||
|
["polls.v5.meet.jitsi"] = "tcp://127.0.0.1:52695";
|
||||||
["conference.v6.meet.jitsi"] = "tcp://127.0.0.1:52696";
|
["conference.v6.meet.jitsi"] = "tcp://127.0.0.1:52696";
|
||||||
["v6.meet.jitsi"] = "tcp://127.0.0.1:52696";
|
["v6.meet.jitsi"] = "tcp://127.0.0.1:52696";
|
||||||
|
["polls.v6.meet.jitsi"] = "tcp://127.0.0.1:52696";
|
||||||
["conference.v7.meet.jitsi"] = "tcp://127.0.0.1:52697";
|
["conference.v7.meet.jitsi"] = "tcp://127.0.0.1:52697";
|
||||||
["v7.meet.jitsi"] = "tcp://127.0.0.1:52697";
|
["v7.meet.jitsi"] = "tcp://127.0.0.1:52697";
|
||||||
|
["polls.v7.meet.jitsi"] = "tcp://127.0.0.1:52697";
|
||||||
["conference.v8.meet.jitsi"] = "tcp://127.0.0.1:52698";
|
["conference.v8.meet.jitsi"] = "tcp://127.0.0.1:52698";
|
||||||
["v8.meet.jitsi"] = "tcp://127.0.0.1:52698";
|
["v8.meet.jitsi"] = "tcp://127.0.0.1:52698";
|
||||||
|
["polls.v8.meet.jitsi"] = "tcp://127.0.0.1:52698";
|
||||||
}
|
}
|
||||||
-- allowed list of server-2-server connections
|
-- allowed list of server-2-server connections
|
||||||
s2s_whitelist = {
|
s2s_whitelist = {
|
||||||
"conference.v1.meet.jitsi", "conference.v2.meet.jitsi", "conference.v3.meet.jitsi", "conference.v4.meet.jitsi",
|
"conference.v1.meet.jitsi", "conference.v2.meet.jitsi", "conference.v3.meet.jitsi", "conference.v4.meet.jitsi",
|
||||||
"conference.v5.meet.jitsi", "conference.v6.meet.jitsi", "conference.v7.meet.jitsi", "conference.v8.meet.jitsi"
|
"conference.v5.meet.jitsi", "conference.v6.meet.jitsi", "conference.v7.meet.jitsi", "conference.v8.meet.jitsi",
|
||||||
|
'polls.v1.meet.jitsi', 'polls.v2.meet.jitsi', 'polls.v3.meet.jitsi', 'polls.v4.meet.jitsi',
|
||||||
|
'polls.v5.meet.jitsi', 'polls.v6.meet.jitsi', 'polls.v7.meet.jitsi', 'polls.v8.meet.jitsi'
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -38,12 +38,14 @@ s2s_whitelist = {
|
|||||||
'conference.jitmeet.example.com', -- needed for visitors to send messages to main room
|
'conference.jitmeet.example.com', -- needed for visitors to send messages to main room
|
||||||
'visitors.jitmeet.example.com'; -- needed for sending promotion request to visitors.jitmeet.example.com component
|
'visitors.jitmeet.example.com'; -- needed for sending promotion request to visitors.jitmeet.example.com component
|
||||||
'jitmeet.example.com'; -- unavailable presences back to main room
|
'jitmeet.example.com'; -- unavailable presences back to main room
|
||||||
|
'polls.jitmeet.example.com'; -- polls component
|
||||||
};
|
};
|
||||||
|
|
||||||
s2sout_override = {
|
s2sout_override = {
|
||||||
["conference.jitmeet.example.com"] = "tcp://127.0.0.1:5269"; -- needed for visitors to send messages to main room
|
["conference.jitmeet.example.com"] = "tcp://127.0.0.1:5269"; -- needed for visitors to send messages to main room
|
||||||
["jitmeet.example.com"] = "tcp://127.0.0.1:5269"; -- needed for the main room when connecting in to send main participants
|
["jitmeet.example.com"] = "tcp://127.0.0.1:5269"; -- needed for the main room when connecting in to send main participants
|
||||||
["visitors.jitmeet.example.com"] = "tcp://127.0.0.1:5269"; -- needed for sending promotion request to visitors.jitmeet.example.com component
|
["visitors.jitmeet.example.com"] = "tcp://127.0.0.1:5269"; -- needed for sending promotion request to visitors.jitmeet.example.com component
|
||||||
|
['polls.jitmeet.example.com'] = "tcp://127.0.0.1:5269"; -- polls component
|
||||||
}
|
}
|
||||||
|
|
||||||
external_service_secret = '__turnSecret__';
|
external_service_secret = '__turnSecret__';
|
||||||
@@ -106,6 +108,7 @@ VirtualHost 'vX.meet.jitsi'
|
|||||||
'smacks';
|
'smacks';
|
||||||
'jiconop';
|
'jiconop';
|
||||||
'conference_duration';
|
'conference_duration';
|
||||||
|
'features_identity';
|
||||||
}
|
}
|
||||||
main_muc = 'conference.vX.meet.jitsi';
|
main_muc = 'conference.vX.meet.jitsi';
|
||||||
|
|
||||||
@@ -138,3 +141,5 @@ Component 'conference.vX.meet.jitsi' 'muc'
|
|||||||
};
|
};
|
||||||
muc_room_locking = false
|
muc_room_locking = false
|
||||||
muc_room_default_public_jids = true
|
muc_room_default_public_jids = true
|
||||||
|
|
||||||
|
Component 'polls.vX.meet.jitsi' 'polls_component'
|
||||||
|
|||||||
@@ -749,6 +749,13 @@ local function iq_from_main_handler(event)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local pollsEl = node:get_child('polls');
|
||||||
|
if pollsEl then
|
||||||
|
local polls = json.decode(pollsEl:get_text());
|
||||||
|
-- let's find is there a new poll
|
||||||
|
module:fire_event('jitsi-polls-update', { room = room; polls = polls; });
|
||||||
|
end
|
||||||
|
|
||||||
if fire_jicofo_unlock then
|
if fire_jicofo_unlock then
|
||||||
-- everything is connected allow participants to join
|
-- everything is connected allow participants to join
|
||||||
module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; });
|
module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; });
|
||||||
@@ -814,6 +821,36 @@ function route_s2s_stanza(event)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module:hook('answer-poll', function(answerData)
|
||||||
|
local room = answerData.room;
|
||||||
|
local room_jid = room_jid_match_rewrite(jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain));
|
||||||
|
|
||||||
|
-- now send it to the main prosody
|
||||||
|
local data = {
|
||||||
|
answers = answerData.data.answers;
|
||||||
|
command = 'answer-poll';
|
||||||
|
pollId = answerData.pollId;
|
||||||
|
roomJid = room_jid,
|
||||||
|
senderId = answerData.voterId;
|
||||||
|
senderName = answerData.voterName;
|
||||||
|
type = 'polls';
|
||||||
|
};
|
||||||
|
|
||||||
|
local data_str, error = json.encode(data);
|
||||||
|
if not data_str then
|
||||||
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
||||||
|
end
|
||||||
|
|
||||||
|
local stanza = st.message({
|
||||||
|
from = module.host,
|
||||||
|
to = 'polls.'..main_domain
|
||||||
|
})
|
||||||
|
:tag("json-message", { xmlns = "http://jitsi.org/jitmeet"; roomJid = room_jid; })
|
||||||
|
:text(data_str)
|
||||||
|
:up();
|
||||||
|
room:route_stanza(stanza);
|
||||||
|
end);
|
||||||
|
|
||||||
-- routing to sessions in mod_s2s is -1 and -10, we want to hook before that to make sure to is correct
|
-- routing to sessions in mod_s2s is -1 and -10, we want to hook before that to make sure to is correct
|
||||||
-- or if we want to filter that stanza
|
-- or if we want to filter that stanza
|
||||||
module:hook("route/remote", route_s2s_stanza, 10);
|
module:hook("route/remote", route_s2s_stanza, 10);
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
-- by keeping track of the state of polls in each room, and sending
|
-- by keeping track of the state of polls in each room, and sending
|
||||||
-- that state to new participants when they join.
|
-- that state to new participants when they join.
|
||||||
|
|
||||||
|
local it = require 'util.iterators';
|
||||||
local json = require 'cjson.safe';
|
local json = require 'cjson.safe';
|
||||||
|
local array = require 'util.array';
|
||||||
local st = require("util.stanza");
|
local st = require("util.stanza");
|
||||||
local jid = require "util.jid";
|
local jid = require "util.jid";
|
||||||
local util = module:require("util");
|
local util = module:require("util");
|
||||||
@@ -12,6 +14,8 @@ local NS_NICK = 'http://jabber.org/protocol/nick';
|
|||||||
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
|
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||||
|
local table_compare = util.table_compare;
|
||||||
|
|
||||||
local POLLS_LIMIT = 128;
|
local POLLS_LIMIT = 128;
|
||||||
local POLL_PAYLOAD_LIMIT = 1024;
|
local POLL_PAYLOAD_LIMIT = 1024;
|
||||||
@@ -23,6 +27,11 @@ if not main_virtual_host then
|
|||||||
end
|
end
|
||||||
local muc_domain_prefix = module:get_option_string('muc_mapper_domain_prefix', 'conference');
|
local muc_domain_prefix = module:get_option_string('muc_mapper_domain_prefix', 'conference');
|
||||||
|
|
||||||
|
-- this is the main virtual host of the main prosody that this vnode serves
|
||||||
|
local main_domain = module:get_option_string('main_domain');
|
||||||
|
-- only the visitor prosody has main_domain setting
|
||||||
|
local is_visitor_prosody = main_domain ~= nil;
|
||||||
|
|
||||||
-- Logs a warning and returns true if a room does not
|
-- Logs a warning and returns true if a room does not
|
||||||
-- have poll data associated with it.
|
-- have poll data associated with it.
|
||||||
local function check_polls(room)
|
local function check_polls(room)
|
||||||
@@ -103,191 +112,283 @@ end
|
|||||||
|
|
||||||
local function send_polls_message_to_all(room, data_str)
|
local function send_polls_message_to_all(room, data_str)
|
||||||
for _, room_occupant in room:each_occupant() do
|
for _, room_occupant in room:each_occupant() do
|
||||||
send_polls_message(room, data_str, room_occupant.jid);
|
-- in case of visitor node send only to visitors
|
||||||
|
if not is_visitor_prosody or room_occupant.role == 'visitor' then
|
||||||
|
send_polls_message(room, data_str, room_occupant.jid);
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Keeps track of the current state of the polls in each room,
|
-- Keeps track of the current state of the polls in each room,
|
||||||
-- by listening to "new-poll" and "answer-poll" messages,
|
-- by listening to "new-poll" and "answer-poll" messages,
|
||||||
-- and updating the room poll data accordingly.
|
-- and updating the room poll data accordingly.
|
||||||
-- This mirrors the client-side poll update logic.
|
-- This mirrors the client-side poll update logic.
|
||||||
module:hook('message/host', function(event)
|
module:hook('message/host', function(event)
|
||||||
local session, stanza = event.origin, event.stanza;
|
local session, stanza = event.origin, event.stanza;
|
||||||
|
|
||||||
-- we are interested in all messages without a body that are not groupchat
|
-- we are interested in all messages without a body that are not groupchat
|
||||||
if stanza.attr.type == 'groupchat' or stanza:get_child('body') then
|
if stanza.attr.type == 'groupchat' or stanza:get_child('body') then
|
||||||
return;
|
return;
|
||||||
end
|
|
||||||
|
|
||||||
local json_message = stanza:get_child('json-message', 'http://jitsi.org/jitmeet')
|
|
||||||
or stanza:get_child('json-message');
|
|
||||||
if not json_message then
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
|
||||||
if not room then
|
|
||||||
module:log('warn', 'No room found found for %s %s', session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
local occupant_jid = stanza.attr.from;
|
|
||||||
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
|
||||||
if not occupant then
|
|
||||||
module:log("error", "Occupant sending msg %s was not found in room %s", occupant_jid, room.jid)
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
local json_message_text = json_message:get_text();
|
|
||||||
if string.len(json_message_text) >= POLL_PAYLOAD_LIMIT then
|
|
||||||
module:log('error', 'Poll payload too large, discarding. Sender: %s to:%s', stanza.attr.from, stanza.attr.to);
|
|
||||||
return true;
|
|
||||||
end
|
|
||||||
|
|
||||||
local data, error = json.decode(json_message_text);
|
|
||||||
if error then
|
|
||||||
module:log('error', 'Error decoding data error:%s Sender: %s to:%s', error, stanza.attr.from, stanza.attr.to);
|
|
||||||
return true;
|
|
||||||
end
|
|
||||||
|
|
||||||
if not data or (data.command ~= "new-poll" and data.command ~= "answer-poll") then
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
if not validate_polls(data) then
|
|
||||||
module:log('error', 'Invalid poll data. Sender: %s (%s)', stanza.attr.from, json_message_text);
|
|
||||||
return true;
|
|
||||||
end
|
|
||||||
|
|
||||||
if data.command == "new-poll" then
|
|
||||||
if check_polls(room) then return end
|
|
||||||
|
|
||||||
local poll_creator = get_occupant_details(occupant)
|
|
||||||
if not poll_creator then
|
|
||||||
module:log("error", "Cannot retrieve poll creator id and name for %s from %s", occupant.jid, room.jid)
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if room.polls.count >= POLLS_LIMIT then
|
local json_message = stanza:get_child('json-message', 'http://jitsi.org/jitmeet')
|
||||||
module:log("error", "Too many polls created in %s", room.jid)
|
or stanza:get_child('json-message');
|
||||||
|
if not json_message then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room;
|
||||||
|
if session.type == 's2sin' then
|
||||||
|
if not json_message.attr.roomJid then
|
||||||
|
module:log('warn', 'No room jid found in %s', stanza);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
room = get_room_from_jid(room_jid_match_rewrite(json_message.attr.roomJid));
|
||||||
|
else
|
||||||
|
room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||||
|
end
|
||||||
|
|
||||||
|
if not room then
|
||||||
|
module:log('warn', 'No room found found for %s %s', session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local json_message_text = json_message:get_text();
|
||||||
|
if string.len(json_message_text) >= POLL_PAYLOAD_LIMIT then
|
||||||
|
module:log('error', 'Poll payload too large, discarding. Sender: %s to:%s', stanza.attr.from, stanza.attr.to);
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
if room.polls.by_id[data.pollId] ~= nil then
|
local data, error = json.decode(json_message_text);
|
||||||
module:log("error", "Poll already exists: %s", data.pollId);
|
if error then
|
||||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Poll already exists'));
|
module:log('error', 'Error decoding data error:%s Sender: %s to:%s', error, stanza.attr.from, stanza.attr.to);
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
if room.jitsiMetadata and room.jitsiMetadata.permissions
|
if not data or (data.command ~= "new-poll" and data.command ~= "answer-poll") then
|
||||||
and room.jitsiMetadata.permissions.pollCreationRestricted
|
return;
|
||||||
and not is_feature_allowed('create-polls', origin.jitsi_meet_context_features) then
|
end
|
||||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Creation of polls not allowed for user'));
|
|
||||||
|
if not validate_polls(data) then
|
||||||
|
module:log('error', 'Invalid poll data. Sender: %s (%s)', stanza.attr.from, json_message_text);
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
local occupant_details;
|
||||||
|
if session.type ~= 's2sin' then
|
||||||
|
local occupant_jid = stanza.attr.from;
|
||||||
|
occupant = room:get_occupant_by_real_jid(occupant_jid);
|
||||||
|
if not occupant then
|
||||||
|
module:log("error", "Occupant sending msg %s was not found in room %s", occupant_jid, room.jid)
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
occupant_details = get_occupant_details(occupant)
|
||||||
|
if not occupant_details then
|
||||||
|
module:log("error", "Cannot retrieve poll creator or voter id and name for %s from %s",
|
||||||
|
occupant.jid, room.jid)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- this is a message from a visitor prosody, we will trust it
|
||||||
|
occupant_details = { occupant_id = data.senderId; occupant_name = data.senderName; };
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.command == "new-poll" then
|
||||||
|
if is_visitor_prosody then
|
||||||
|
module:log("error", "Poll cannot be created on visitor node.");
|
||||||
|
session.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Poll cannot be created by visitor node'));
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
local answers = {}
|
if check_polls(room) then return end
|
||||||
local compact_answers = {}
|
|
||||||
for i, a in ipairs(data.answers) do
|
|
||||||
table.insert(answers, { name = a.name });
|
|
||||||
table.insert(compact_answers, { key = i, name = a.name});
|
|
||||||
end
|
|
||||||
|
|
||||||
local poll = {
|
local poll_creator = occupant_details;
|
||||||
pollId = data.pollId,
|
|
||||||
senderId = poll_creator.occupant_id,
|
|
||||||
senderName = poll_creator.occupant_name,
|
|
||||||
question = data.question,
|
|
||||||
answers = answers
|
|
||||||
};
|
|
||||||
|
|
||||||
room.polls.by_id[data.pollId] = poll
|
if room.polls.count >= POLLS_LIMIT then
|
||||||
table.insert(room.polls.order, poll)
|
module:log("error", "Too many polls created in %s", room.jid)
|
||||||
room.polls.count = room.polls.count + 1;
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
local pollData = {
|
if room.polls.by_id[data.pollId] ~= nil then
|
||||||
event = event,
|
module:log("error", "Poll already exists: %s", data.pollId);
|
||||||
room = room,
|
session.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Poll already exists'));
|
||||||
poll = {
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
if room.jitsiMetadata and room.jitsiMetadata.permissions
|
||||||
|
and room.jitsiMetadata.permissions.pollCreationRestricted
|
||||||
|
and not is_feature_allowed('create-polls', session.jitsi_meet_context_features) then
|
||||||
|
session.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Creation of polls not allowed for user'));
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
local answers = {}
|
||||||
|
local compact_answers = {}
|
||||||
|
for i, a in ipairs(data.answers) do
|
||||||
|
table.insert(answers, { name = a.name });
|
||||||
|
table.insert(compact_answers, { key = i, name = a.name});
|
||||||
|
end
|
||||||
|
|
||||||
|
local poll = {
|
||||||
pollId = data.pollId,
|
pollId = data.pollId,
|
||||||
senderId = poll_creator.occupant_id,
|
senderId = poll_creator.occupant_id,
|
||||||
senderName = poll_creator.occupant_name,
|
senderName = poll_creator.occupant_name,
|
||||||
question = data.question,
|
question = data.question,
|
||||||
answers = compact_answers
|
answers = answers
|
||||||
|
};
|
||||||
|
|
||||||
|
room.polls.by_id[data.pollId] = poll
|
||||||
|
table.insert(room.polls.order, poll)
|
||||||
|
room.polls.count = room.polls.count + 1;
|
||||||
|
|
||||||
|
local pollData = {
|
||||||
|
event = event,
|
||||||
|
room = room,
|
||||||
|
poll = {
|
||||||
|
pollId = data.pollId,
|
||||||
|
senderId = poll_creator.occupant_id,
|
||||||
|
senderName = poll_creator.occupant_name,
|
||||||
|
question = data.question,
|
||||||
|
answers = compact_answers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
module:context(jid.host(room.jid)):fire_event('poll-created', pollData);
|
-- now send message to all participants
|
||||||
|
data.senderId = poll_creator.occupant_id;
|
||||||
-- now send message to all participants
|
data.type = 'polls';
|
||||||
data.senderId = poll_creator.occupant_id;
|
local json_msg_str, error = json.encode(data);
|
||||||
data.type = 'polls';
|
if not json_msg_str then
|
||||||
local json_msg_str, error = json.encode(data);
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
||||||
if not json_msg_str then
|
|
||||||
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
|
||||||
end
|
|
||||||
send_polls_message_to_all(room, json_msg_str);
|
|
||||||
elseif data.command == "answer-poll" then
|
|
||||||
if check_polls(room) then return end
|
|
||||||
|
|
||||||
local poll = room.polls.by_id[data.pollId];
|
|
||||||
if poll == nil then
|
|
||||||
module:log("warn", "answering inexistent poll");
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
local voter = get_occupant_details(occupant)
|
|
||||||
if not voter then
|
|
||||||
module:log("error", "Cannot retrieve voter id and name for %s from %s", occupant.jid, room.jid)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local answers = {};
|
|
||||||
for vote_option_idx, vote_flag in ipairs(data.answers) do
|
|
||||||
local answer = poll.answers[vote_option_idx]
|
|
||||||
|
|
||||||
table.insert(answers, {
|
|
||||||
key = vote_option_idx,
|
|
||||||
value = vote_flag,
|
|
||||||
name = answer.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
if vote_flag then
|
|
||||||
local voters = answer.voters;
|
|
||||||
if not voters then
|
|
||||||
answer.voters = {};
|
|
||||||
voters = answer.voters;
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(voters, {
|
|
||||||
id = voter.occupant_id;
|
|
||||||
name = vote_flag and voter.occupant_name or nil;
|
|
||||||
});
|
|
||||||
end
|
end
|
||||||
end
|
send_polls_message_to_all(room, json_msg_str);
|
||||||
|
|
||||||
local answerData = {
|
module:context(jid.host(room.jid)):fire_event('poll-created', pollData);
|
||||||
event = event,
|
elseif data.command == "answer-poll" then
|
||||||
room = room,
|
if check_polls(room) then return end
|
||||||
pollId = poll.pollId,
|
|
||||||
voterName = voter.occupant_name,
|
|
||||||
voterId = voter.occupant_id,
|
|
||||||
answers = answers
|
|
||||||
}
|
|
||||||
module:context(jid.host(room.jid)):fire_event("answer-poll", answerData);
|
|
||||||
|
|
||||||
data.senderId = voter.occupant_id;
|
local poll = room.polls.by_id[data.pollId];
|
||||||
data.type = 'polls';
|
if poll == nil then
|
||||||
local json_msg_str, error = json.encode(data);
|
module:log("warn", "answering inexistent poll %s", data.pollId);
|
||||||
if not json_msg_str then
|
return;
|
||||||
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
end
|
||||||
|
|
||||||
|
local voter = occupant_details;
|
||||||
|
|
||||||
|
local answers = {};
|
||||||
|
for vote_option_idx, vote_flag in ipairs(data.answers) do
|
||||||
|
local answer = poll.answers[vote_option_idx]
|
||||||
|
|
||||||
|
table.insert(answers, {
|
||||||
|
key = vote_option_idx,
|
||||||
|
value = vote_flag,
|
||||||
|
name = answer.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
if vote_flag then
|
||||||
|
local voters = answer.voters;
|
||||||
|
if not voters then
|
||||||
|
answer.voters = {};
|
||||||
|
voters = answer.voters;
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(voters, {
|
||||||
|
id = voter.occupant_id;
|
||||||
|
name = vote_flag and voter.occupant_name or nil;
|
||||||
|
});
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local answerData = {
|
||||||
|
data = data,
|
||||||
|
event = event,
|
||||||
|
room = room,
|
||||||
|
pollId = poll.pollId,
|
||||||
|
voterName = voter.occupant_name,
|
||||||
|
voterId = voter.occupant_id,
|
||||||
|
answers = answers,
|
||||||
|
}
|
||||||
|
|
||||||
|
data.senderId = voter.occupant_id;
|
||||||
|
data.senderName = voter.occupant_name;
|
||||||
|
data.type = 'polls';
|
||||||
|
local json_msg_str, error = json.encode(data);
|
||||||
|
if not json_msg_str then
|
||||||
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
send_polls_message_to_all(room, json_msg_str);
|
||||||
|
|
||||||
|
module:context(jid.host(room.jid)):fire_event('answer-poll', answerData);
|
||||||
end
|
end
|
||||||
send_polls_message_to_all(room, json_msg_str);
|
|
||||||
end
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
-- Find in which poll in newPolls we have updated answers
|
||||||
|
-- @returns poll, senderId, array of boolean values for the answers of this sender
|
||||||
|
function find_updated_poll(oldPolls, newPolls)
|
||||||
|
for _, v in pairs(newPolls) do
|
||||||
|
local existing_poll = oldPolls[v.pollId];
|
||||||
|
local senderId;
|
||||||
|
|
||||||
|
for idx, newAnswer in ipairs(v.answers) do
|
||||||
|
-- let's examine now the voters
|
||||||
|
-- Create lookup tables using id as key for efficient searching
|
||||||
|
local oldLookup = {}
|
||||||
|
local newLookup = {}
|
||||||
|
|
||||||
|
-- Build lookup table for old array
|
||||||
|
if existing_poll.answers[idx].voters then
|
||||||
|
for _, element in ipairs(existing_poll.answers[idx].voters) do
|
||||||
|
oldLookup[element.id] = element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Build lookup table for new array
|
||||||
|
if newAnswer.voters then
|
||||||
|
for _, element in ipairs(newAnswer.voters) do
|
||||||
|
newLookup[element.id] = element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find removed elements (in old but not in new)
|
||||||
|
if existing_poll.answers[idx].voters then
|
||||||
|
for _, element in ipairs(existing_poll.answers[idx].voters) do
|
||||||
|
if not newLookup[element.id] then
|
||||||
|
senderId = element.id;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find added elements (in new but not in old)
|
||||||
|
if newAnswer.voters then
|
||||||
|
for _, element in ipairs(newAnswer.voters) do
|
||||||
|
if not oldLookup[element.id] then
|
||||||
|
senderId = element.id;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if senderId ~= nil then
|
||||||
|
-- an array of true/false values for this sender
|
||||||
|
local senderAnswers = {};
|
||||||
|
for idx, newAnswer in ipairs(v.answers) do
|
||||||
|
senderAnswers[idx] = false;
|
||||||
|
if newAnswer.voters then
|
||||||
|
for _, element in ipairs(newAnswer.voters) do
|
||||||
|
if element.id == senderId then
|
||||||
|
senderAnswers[idx] = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return v, senderId, senderAnswers;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local setup_muc_component = function(host_module, host)
|
local setup_muc_component = function(host_module, host)
|
||||||
-- Sets up poll data in new rooms.
|
-- Sets up poll data in new rooms.
|
||||||
host_module:hook("muc-room-created", function(event)
|
host_module:hook("muc-room-created", function(event)
|
||||||
@@ -329,6 +430,82 @@ local setup_muc_component = function(host_module, host)
|
|||||||
end
|
end
|
||||||
send_polls_message(room, json_msg_str, event.occupant.jid);
|
send_polls_message(room, json_msg_str, event.occupant.jid);
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
-- Handles poll updates coming for a visitor node, the event contain polls structure
|
||||||
|
-- like 'old-polls' one
|
||||||
|
host_module:hook('jitsi-polls-update', function(event)
|
||||||
|
local polls_command, room = event.polls, event.room;
|
||||||
|
-- this is the initial state coming from the main prosody when only jicofo is in the room
|
||||||
|
if room.polls.count == 0 and it.count(room:each_occupant()) == 1 then
|
||||||
|
for i, v in ipairs(polls_command.polls) do
|
||||||
|
room.polls.by_id[v.pollId] = v;
|
||||||
|
table.insert(room.polls.order, v);
|
||||||
|
room.polls.count = room.polls.count + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- at this point we need to find which is the new poll
|
||||||
|
local new_poll;
|
||||||
|
for _, v in pairs(polls_command.polls) do
|
||||||
|
if not room.polls.by_id[v.pollId] then
|
||||||
|
new_poll = v;
|
||||||
|
break;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not new_poll then
|
||||||
|
-- this is an update of the voters in some of the existing polls
|
||||||
|
local updatedPoll, senderId, answers = find_updated_poll(room.polls.by_id, polls_command.polls);
|
||||||
|
|
||||||
|
if not updatedPoll then
|
||||||
|
module:log('warn', 'no new or updated poll found in update for room %s', room.jid);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local data = {
|
||||||
|
answers = answers,
|
||||||
|
command = 'answer-poll',
|
||||||
|
pollId = updatedPoll.pollId,
|
||||||
|
senderId = senderId,
|
||||||
|
roomJid = internal_room_jid_match_rewrite(room.jid),
|
||||||
|
type = 'polls'
|
||||||
|
};
|
||||||
|
|
||||||
|
-- we need to update the history
|
||||||
|
room.polls.by_id[updatedPoll.pollId].answers = updatedPoll.answers;
|
||||||
|
|
||||||
|
local json_msg_str, error = json.encode(data);
|
||||||
|
if not json_msg_str then
|
||||||
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
||||||
|
end
|
||||||
|
send_polls_message_to_all(room, json_msg_str);
|
||||||
|
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
room.polls.by_id[new_poll.pollId] = new_poll;
|
||||||
|
table.insert(room.polls.order, new_poll);
|
||||||
|
room.polls.count = room.polls.count + 1;
|
||||||
|
|
||||||
|
local data = {
|
||||||
|
answers = new_poll.answers,
|
||||||
|
command = 'new-poll',
|
||||||
|
pollId = new_poll.pollId,
|
||||||
|
question = new_poll.question,
|
||||||
|
senderId = new_poll.senderId,
|
||||||
|
roomJid = internal_room_jid_match_rewrite(room.jid),
|
||||||
|
type = 'polls'
|
||||||
|
};
|
||||||
|
|
||||||
|
local json_msg_str, error = json.encode(data);
|
||||||
|
if not json_msg_str then
|
||||||
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
||||||
|
end
|
||||||
|
|
||||||
|
send_polls_message_to_all(room, json_msg_str);
|
||||||
|
end);
|
||||||
end
|
end
|
||||||
|
|
||||||
process_host_module(muc_domain_prefix..'.'..main_virtual_host, setup_muc_component);
|
process_host_module(muc_domain_prefix..'.'..main_virtual_host, setup_muc_component);
|
||||||
|
|||||||
@@ -94,6 +94,31 @@ local function send_visitors_iq(conference_service, room, type)
|
|||||||
end
|
end
|
||||||
visitors_iq:up();
|
visitors_iq:up();
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if room.polls and room.polls.count > 0 then
|
||||||
|
-- polls created in the room that we want to send to the visitor nodes
|
||||||
|
local data = {
|
||||||
|
command = "old-polls",
|
||||||
|
polls = {},
|
||||||
|
type = 'polls'
|
||||||
|
};
|
||||||
|
for i, poll in ipairs(room.polls.order) do
|
||||||
|
data.polls[i] = {
|
||||||
|
pollId = poll.pollId,
|
||||||
|
senderId = poll.senderId,
|
||||||
|
senderName = poll.senderName,
|
||||||
|
question = poll.question,
|
||||||
|
answers = poll.answers
|
||||||
|
};
|
||||||
|
end
|
||||||
|
|
||||||
|
local json_msg_str, error = json.encode(data);
|
||||||
|
if not json_msg_str then
|
||||||
|
module:log('error', 'Error encoding data room:%s error:%s', room.jid, error);
|
||||||
|
end
|
||||||
|
|
||||||
|
visitors_iq:tag('polls'):text(json_msg_str):up();
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
visitors_iq:up();
|
visitors_iq:up();
|
||||||
@@ -472,6 +497,38 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end, -2);
|
end, -2);
|
||||||
|
|
||||||
|
host_module:hook('poll-created', function (event)
|
||||||
|
local room = event.room;
|
||||||
|
|
||||||
|
if not visitors_nodes[room.jid] then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we need to update all vnodes
|
||||||
|
local vnodes = visitors_nodes[room.jid].nodes;
|
||||||
|
for conference_service in pairs(vnodes) do
|
||||||
|
send_visitors_iq(conference_service, room, 'update');
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
host_module:hook('answer-poll', function (event)
|
||||||
|
local room, stanza = event.room, event.event.stanza;
|
||||||
|
|
||||||
|
if not visitors_nodes[room.jid] then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local from = stanza.attr.from;
|
||||||
|
|
||||||
|
-- we need to update all vnodes
|
||||||
|
local vnodes = visitors_nodes[room.jid].nodes;
|
||||||
|
for conference_service in pairs(vnodes) do
|
||||||
|
-- skip sending the answer to the node from where it originates
|
||||||
|
if conference_service ~= from then
|
||||||
|
send_visitors_iq(conference_service, room, 'update');
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end);
|
||||||
end);
|
end);
|
||||||
|
|
||||||
local function update_vnodes_for_room(event)
|
local function update_vnodes_for_room(event)
|
||||||
|
|||||||
@@ -213,6 +213,9 @@ end
|
|||||||
local function disconnect_vnode_received(room, vnode)
|
local function disconnect_vnode_received(room, vnode)
|
||||||
module:context(muc_domain_base):fire_event('jitsi-disconnect-vnode', { room = room; vnode = vnode; });
|
module:context(muc_domain_base):fire_event('jitsi-disconnect-vnode', { room = room; vnode = vnode; });
|
||||||
|
|
||||||
|
if not room._connected_vnodes then
|
||||||
|
return;
|
||||||
|
end
|
||||||
room._connected_vnodes:set(vnode..'.meet.jitsi', nil);
|
room._connected_vnodes:set(vnode..'.meet.jitsi', nil);
|
||||||
|
|
||||||
if room._connected_vnodes:count() == 0 then
|
if room._connected_vnodes:count() == 0 then
|
||||||
|
|||||||
Reference in New Issue
Block a user