From c19a8ef9ff319d31991e1dd8a23bdd9efb9389bb Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Wed, 31 Oct 2018 13:13:05 +0000
Subject: MUC: Announce affiliation changes for JIDs that are not in the room

---
 plugins/muc/muc.lib.lua  | 35 +++++++++++++++++++++++------------
 plugins/muc/util.lib.lua |  9 +++++++++
 2 files changed, 32 insertions(+), 12 deletions(-)

(limited to 'plugins')

diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua
index f69e518b..65f339ed 100644
--- a/plugins/muc/muc.lib.lua
+++ b/plugins/muc/muc.lib.lua
@@ -1313,20 +1313,31 @@ function room_mt:set_affiliation(actor, jid, affiliation, reason, data)
 		end
 	end
 	local is_semi_anonymous = self:get_whois() == "moderators";
-	for occupant, old_role in pairs(occupants_updated) do
-		self:publicise_occupant_status(occupant, x, nil, actor, reason);
-		if occupant.role == nil then
-			module:fire_event("muc-occupant-left", {room = self; nick = occupant.nick; occupant = occupant;});
-		elseif is_semi_anonymous and
-			(old_role == "moderator" and occupant.role ~= "moderator") or
-			(old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status
-			-- Send everyone else's presences (as jid visibility has changed)
-			for real_jid in occupant:each_session() do
-				self:send_occupant_list(real_jid, function(occupant_jid, occupant) --luacheck: ignore 212 433
-					return occupant.bare_jid ~= jid;
-				end);
+
+	if next(occupants_updated) ~= nil then
+		for occupant, old_role in pairs(occupants_updated) do
+			self:publicise_occupant_status(occupant, x, nil, actor, reason);
+			if occupant.role == nil then
+				module:fire_event("muc-occupant-left", {room = self; nick = occupant.nick; occupant = occupant;});
+			elseif is_semi_anonymous and
+				(old_role == "moderator" and occupant.role ~= "moderator") or
+				(old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status
+				-- Send everyone else's presences (as jid visibility has changed)
+				for real_jid in occupant:each_session() do
+					self:send_occupant_list(real_jid, function(occupant_jid, occupant) --luacheck: ignore 212 433
+						return occupant.bare_jid ~= jid;
+					end);
+				end
 			end
 		end
+	else
+		-- Announce affiliation change for a user that is not currently in the room,
+		-- XEP-0045 (v1.31.2) example 195
+		-- add_item(x, affiliation, role, jid, nick, actor_nick, actor_jid, reason)
+		local announce_msg = st.message({ from = self.jid })
+			:add_child(add_item(st.clone(x), affiliation, nil, jid, nil, nil, nil, reason));
+		local min_role = is_semi_anonymous and "moderator" or "none";
+		self:broadcast(announce_msg, muc_util.only_with_min_role(min_role));
 	end
 
 	self:save(true);
diff --git a/plugins/muc/util.lib.lua b/plugins/muc/util.lib.lua
index 16deb543..d03b4d6b 100644
--- a/plugins/muc/util.lib.lua
+++ b/plugins/muc/util.lib.lua
@@ -55,4 +55,13 @@ function _M.filter_muc_x(stanza)
 	return stanza:maptags(muc_x_filter);
 end
 
+function _M.only_with_min_role(role)
+	local min_role_value = _M.valid_roles[role];
+	return function (nick, occupant)
+		if _M.valid_roles[occupant.role or "none"] >= min_role_value then
+			return true;
+		end
+	end;
+end
+
 return _M;
-- 
cgit v1.2.3