social_stream-presence 0.8.4 → 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/javascripts/chat_interface_manager.js.erb +42 -132
- data/app/assets/javascripts/chat_persistence.js +84 -70
- data/app/assets/javascripts/chat_utilities.js +10 -33
- data/app/assets/javascripts/chat_window_manager.js +275 -217
- data/app/assets/javascripts/jquery.ui.chatbox.sstreampresence.js +9 -2
- data/app/assets/javascripts/notifications.js +191 -0
- data/app/assets/javascripts/social_stream-presence.js +1 -0
- data/app/assets/javascripts/videochat.js.erb +4 -24
- data/app/assets/javascripts/xmpp_client_management.js.erb +369 -47
- data/app/assets/stylesheets/chat.css.scss +1 -1
- data/app/views/chat/_contacts.html.erb +5 -5
- data/app/views/chat/_index.html.erb +10 -2
- data/config/locales/en.yml +9 -1
- data/config/locales/es.yml +9 -1
- data/ejabberd/ejabberd_files.zip +0 -0
- data/ejabberd/ejabberd_scripts/emanagement +132 -2
- data/ejabberd/installer.sh +1 -0
- data/ejabberd/mod_muc_admin/mod_muc_admin.beam +0 -0
- data/ejabberd/mod_muc_admin/mod_muc_admin.erl +871 -0
- data/ejabberd/mod_sspresence/mod_sspresence.beam +0 -0
- data/lib/social_stream-presence.rb +1 -1
- data/lib/social_stream/presence/engine.rb +6 -0
- data/lib/social_stream/presence/models/buddy_manager.rb +32 -26
- data/lib/social_stream/presence/models/group_manager.rb +51 -0
- data/lib/social_stream/presence/version.rb +1 -1
- data/lib/social_stream/presence/xmpp_server_order.rb +84 -10
- data/lib/tasks/presence/synchronize.rake +15 -0
- data/vendor/assets/javascripts/strophe.muc.js +934 -0
- metadata +9 -24
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
<div class="users_connected">
|
4
4
|
<%@all_contacts.each do |contact| %>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
<% if contact.user.connected %>
|
6
|
+
<div class="user_presence" name='<%=contact.name%>' slug='<%=contact.slug%>' connected='true'>
|
7
|
+
<% else %>
|
8
|
+
<div class="user_presence" name='<%=contact.name%>' slug='<%=contact.slug%>' connected='false'>
|
9
|
+
<% end %>
|
10
10
|
<a title='<%=contact.name%>' class="presence_user_link" width="28">
|
11
11
|
<%=image_tag(contact.logo.url, :alt => contact.name , :size => "28x28")%>
|
12
12
|
<% if contact.user.status %>
|
@@ -80,9 +80,10 @@
|
|
80
80
|
//Global variables
|
81
81
|
var cookie = "AuthenticationByCookie>>" + "<%=cookies[Rails.application.config.session_options[:key]]%>";
|
82
82
|
var BOSH_SERVICE = '<%= SocialStream::Presence.bosh_service || root_url + "http-bind/" %>';
|
83
|
-
|
83
|
+
var user_name = '<%=current_user.name%>';
|
84
84
|
var user_slug = '<%=current_user.slug%>';
|
85
85
|
var user_jid = '<%=current_user.slug%>'+"@"+domain;
|
86
|
+
|
86
87
|
|
87
88
|
|
88
89
|
$(document).ready(function () {
|
@@ -110,7 +111,7 @@
|
|
110
111
|
|
111
112
|
</script>
|
112
113
|
|
113
|
-
<% if flow %>
|
114
|
+
<% if defined?(flow)!=nil and flow %>
|
114
115
|
<script type="text/javascript">
|
115
116
|
$(document).ready(function () {
|
116
117
|
createMainChatBox();
|
@@ -121,4 +122,11 @@
|
|
121
122
|
<% end %>
|
122
123
|
|
123
124
|
|
125
|
+
<% if defined?(group)!=nil and group %>
|
126
|
+
<script type="text/javascript">
|
127
|
+
var current_group_slug = '<%=group.slug%>';
|
128
|
+
</script>
|
129
|
+
<% end %>
|
130
|
+
|
131
|
+
|
124
132
|
<% end %>
|
data/config/locales/en.yml
CHANGED
@@ -42,4 +42,12 @@ en:
|
|
42
42
|
guestOffline: "Unable to connect. {{name}} is offline"
|
43
43
|
offline: "Unable to connect. You are offline"
|
44
44
|
cancel: "{{name}} cancel the videocall"
|
45
|
-
requirements: You don't have the minimum requirements to run videocall application. Please upgrade to the latest version of Flash.
|
45
|
+
requirements: You don't have the minimum requirements to run videocall application. Please upgrade to the latest version of Flash.
|
46
|
+
muc:
|
47
|
+
group: "{{group}} group"
|
48
|
+
join: "{{nick}} joined in the room."
|
49
|
+
leave: "{{nick}} has left the room."
|
50
|
+
occupants: "Occupants"
|
51
|
+
joining: "Joining..."
|
52
|
+
joinError: "You can't join in the room"
|
53
|
+
offline: "You are offline"
|
data/config/locales/es.yml
CHANGED
@@ -42,4 +42,12 @@ es:
|
|
42
42
|
guestOffline: "Imposible conectar. {{name}} está desconectado"
|
43
43
|
offline: "Imposible conectar. Estás desconectado"
|
44
44
|
cancel: "{{name}} canceló la videollamada"
|
45
|
-
requirements: No tienes los requisitos minimos para iniciar la aplicación de videoconferencia. Por favor, actualiza a la última versión de Flash.
|
45
|
+
requirements: No tienes los requisitos minimos para iniciar la aplicación de videoconferencia. Por favor, actualiza a la última versión de Flash.
|
46
|
+
muc:
|
47
|
+
group: "Grupo {{group}}"
|
48
|
+
join: "{{nick}} ha entrado en la sala."
|
49
|
+
leave: "{{nick}} se ha ido de la sala."
|
50
|
+
occupants: "Miembros"
|
51
|
+
joining: "Accediendo..."
|
52
|
+
joinError: "No se pudo acceder a la sala."
|
53
|
+
offline: "Estás desconectado"
|
data/ejabberd/ejabberd_files.zip
CHANGED
Binary file
|
@@ -5,7 +5,7 @@
|
|
5
5
|
#Ejabberd Management script
|
6
6
|
#New features for management and maintenance ejabberd
|
7
7
|
#@author Aldo Gordillo < agordillos@gmail.com >
|
8
|
-
#@version 2.
|
8
|
+
#@version 2.1 - 28-2-2012
|
9
9
|
####################################
|
10
10
|
|
11
11
|
require 'logger'
|
@@ -48,6 +48,7 @@ PARAMS_FOR_COMMANDS = {
|
|
48
48
|
'removeAllRosters' => 0,
|
49
49
|
'getAllUserJidsWithRosterByDomain' => 1,
|
50
50
|
'printAllRostersByDomain' => 1,
|
51
|
+
'printAllRosters' => 0,
|
51
52
|
'printAllBidirecctionalBuddysByDomain' => 1,
|
52
53
|
'checkUserJid' => 1,
|
53
54
|
'checkBidirecctionalBuddys' => 2,
|
@@ -62,6 +63,16 @@ PARAMS_FOR_COMMANDS = {
|
|
62
63
|
'getConnectedJidsByDomain' => 1,
|
63
64
|
'getConnectedJids' => 0,
|
64
65
|
'kickUserJid' => 1,
|
66
|
+
'createPersistentRoom' => 2,
|
67
|
+
'createRoom' => 2,
|
68
|
+
'destroyRoom' => 2,
|
69
|
+
'destroyAllRoomsByDomain' => 1,
|
70
|
+
'destroyAllRooms' => 0,
|
71
|
+
'getAllJidsOfRoom' => 2,
|
72
|
+
'getAffiliationsOfRoom' => 2,
|
73
|
+
'setJidAffiliationOfRoom' => 4,
|
74
|
+
'printAllRoomsByDomain' => 1,
|
75
|
+
'printAllRooms' => 0,
|
65
76
|
'help' => 0,
|
66
77
|
}
|
67
78
|
|
@@ -76,6 +87,7 @@ SYNTAX_FOR_COMMANDS = {
|
|
76
87
|
'removeAllRosters' => 'removeAllRosters',
|
77
88
|
'getAllUserJidsWithRosterByDomain' => 'getAllUserJidsWithRosterByDomain domain',
|
78
89
|
'printAllRostersByDomain' => 'printAllRostersByDomain domain',
|
90
|
+
'printAllRosters' => 'printAllRosters',
|
79
91
|
'printAllBidirecctionalBuddysByDomain' => 'printAllBidirecctionalBuddysByDomain domain',
|
80
92
|
'checkUserJid' => 'checkUserJid userJid',
|
81
93
|
'checkBidirecctionalBuddys' => 'checkBidirecctionalBuddys userAJid userBJid',
|
@@ -90,6 +102,16 @@ SYNTAX_FOR_COMMANDS = {
|
|
90
102
|
'getConnectedJidsByDomain' => 'getConnectedJidsByDomain domain',
|
91
103
|
'getConnectedJids' => 'getConnectedJids',
|
92
104
|
'kickUserJid' => 'kickUserJid(userJid)',
|
105
|
+
'createPersistentRoom' => 'createPersistentRoom roomName domain',
|
106
|
+
'createRoom' => 'createRoom roomName domain',
|
107
|
+
'destroyRoom' => 'destroyRoom roomName domain',
|
108
|
+
'destroyAllRoomsByDomain' => 'destroyAllRoomsByDomain domain',
|
109
|
+
'destroyAllRooms' => 'destroyAllRooms',
|
110
|
+
'getAllJidsOfRoom' => 'getAllJidsOfRoom roomName domain',
|
111
|
+
'getAffiliationsOfRoom' => 'getAffiliationsOfRoom roomName domain',
|
112
|
+
'setJidAffiliationOfRoom' => 'setJidAffiliationOfRoom roomName domain jid affiliation (affiliation value: owner/admin/member/outcast/none)',
|
113
|
+
'printAllRoomsByDomain' => 'printAllRoomsByDomain domain',
|
114
|
+
'printAllRooms' => 'printAllRooms',
|
93
115
|
'help' => 'help',
|
94
116
|
}
|
95
117
|
|
@@ -546,7 +568,115 @@ end
|
|
546
568
|
|
547
569
|
|
548
570
|
|
571
|
+
#########################
|
572
|
+
#MUC Methods (Multi-user-chat)
|
573
|
+
#########################
|
574
|
+
|
575
|
+
$muc_host = "conference"
|
576
|
+
|
577
|
+
def printAllRoomsByDomain(domain)
|
578
|
+
if(domain=="all")
|
579
|
+
return printAllRooms
|
580
|
+
end
|
581
|
+
output = executeCommand("ejabberdctl muc_online_rooms " + domain)
|
582
|
+
return output;
|
583
|
+
end
|
584
|
+
|
585
|
+
def printAllRooms
|
586
|
+
output = executeCommand("ejabberdctl muc_online_rooms global")
|
587
|
+
return output;
|
588
|
+
end
|
589
|
+
|
590
|
+
def createPersistentRoom(roomName,domain)
|
591
|
+
createRoom(roomName,domain)
|
592
|
+
setRoomPersistence(roomName,domain,true)
|
593
|
+
return "Done"
|
594
|
+
end
|
595
|
+
|
596
|
+
def createRoom(roomName,domain)
|
597
|
+
executeCommand("ejabberdctl create_room " + roomName + " " + $muc_host + "." + domain + " " + domain)
|
598
|
+
return "Done"
|
599
|
+
end
|
600
|
+
|
601
|
+
def setRoomPersistence(roomName,domain,persistence)
|
602
|
+
if (persistence==true)
|
603
|
+
executeCommand("ejabberdctl change_room_option " + roomName + " " + $muc_host + "." + domain + " persistent true")
|
604
|
+
else
|
605
|
+
executeCommand("ejabberdctl change_room_option " + roomName + " " + $muc_host + "." + domain + " persistent false")
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
def destroyAllRoomsByDomain(domain)
|
610
|
+
rooms = getAllRoomsByDomain(domain)
|
611
|
+
rooms.each do |room|
|
612
|
+
destroyRoom(getNameFromRoom(room),getDomainFromRoom(room))
|
613
|
+
end
|
614
|
+
return "Done"
|
615
|
+
end
|
616
|
+
|
617
|
+
def destroyAllRooms()
|
618
|
+
return destroyAllRoomsByDomain("all")
|
619
|
+
end
|
620
|
+
|
621
|
+
def destroyRoom(roomName,domain)
|
622
|
+
executeCommand("ejabberdctl destroy_room " + roomName + " " + $muc_host + "." + domain + " " + domain)
|
623
|
+
return "Done"
|
624
|
+
end
|
625
|
+
|
626
|
+
def getAllRoomsByDomain(domain)
|
627
|
+
if(domain=="all")
|
628
|
+
output = executeCommand("ejabberdctl muc_online_rooms global")
|
629
|
+
else
|
630
|
+
output = executeCommand("ejabberdctl muc_online_rooms " + domain)
|
631
|
+
end
|
632
|
+
rooms = output.split("\n")
|
633
|
+
return rooms
|
634
|
+
end
|
635
|
+
|
636
|
+
def getAllJidsOfRoomObject(room)
|
637
|
+
return getAllJidsOfRoom(getNameFromRoom(room),getDomainFromRoom(room))
|
638
|
+
end
|
639
|
+
|
640
|
+
def getAllJidsOfRoom(roomName,domain)
|
641
|
+
jids = []
|
642
|
+
output = executeCommand("ejabberdctl get_room_occupants " + roomName + " " + $muc_host + "." + domain)
|
643
|
+
lines = output.split("\n")
|
644
|
+
lines.each do |line|
|
645
|
+
jids << line.split("/")[0]
|
646
|
+
end
|
647
|
+
return jids
|
648
|
+
end
|
549
649
|
|
650
|
+
def getAffiliationsOfRoomObject(room)
|
651
|
+
return getAffiliationsOfRoom(getNameFromRoom(room),getDomainFromRoom(room))
|
652
|
+
end
|
653
|
+
|
654
|
+
def getAffiliationsOfRoom(roomName,domain)
|
655
|
+
affiliateds = []
|
656
|
+
output = executeCommand("ejabberdctl get_room_affiliations " + roomName + " " + $muc_host + "." + domain)
|
657
|
+
lines = output.split("\n")
|
658
|
+
lines.each do |line|
|
659
|
+
split=line.split(" ");
|
660
|
+
jid = split[0]+"@"+split[1];
|
661
|
+
affiliation=split[2]
|
662
|
+
affiliateds << [jid,affiliation]
|
663
|
+
end
|
664
|
+
return affiliateds
|
665
|
+
end
|
666
|
+
|
667
|
+
#affiliation = Owner/Admin/Member/Outcast/None
|
668
|
+
def setJidAffiliationOfRoom(roomName,domain,jid,affiliation)
|
669
|
+
output = executeCommand("ejabberdctl set_room_affiliation " + roomName + " " + $muc_host + "." + domain + " " + jid + " " + affiliation)
|
670
|
+
return "Done"
|
671
|
+
end
|
672
|
+
|
673
|
+
def getDomainFromRoom(room)
|
674
|
+
return room.split(".")[1];
|
675
|
+
end
|
676
|
+
|
677
|
+
def getNameFromRoom(room)
|
678
|
+
return room.split("@")[0];
|
679
|
+
end
|
550
680
|
|
551
681
|
|
552
682
|
#########################
|
@@ -558,7 +688,7 @@ def executeCommand(command)
|
|
558
688
|
|
559
689
|
if $verbose
|
560
690
|
#Logging...
|
561
|
-
|
691
|
+
puts "Executing: " + command
|
562
692
|
ejabberdLog("Executing (#{command})")
|
563
693
|
end
|
564
694
|
|
data/ejabberd/installer.sh
CHANGED
@@ -216,6 +216,7 @@ done
|
|
216
216
|
msg "Copying Ejabberd modules"
|
217
217
|
cp $installer_folder_path/mod_admin_extra/mod_admin_extra.beam $ejabberd_module_path
|
218
218
|
cp $installer_folder_path/mod_sspresence/mod_sspresence.beam $ejabberd_module_path
|
219
|
+
cp $installer_folder_path/mod_muc_admin/mod_muc_admin.beam $ejabberd_module_path
|
219
220
|
|
220
221
|
msg "Copying scripts"
|
221
222
|
cp -r $installer_folder_path/ejabberd_scripts/* $scripts_path
|
Binary file
|
@@ -0,0 +1,871 @@
|
|
1
|
+
%%%----------------------------------------------------------------------
|
2
|
+
%%% File : mod_muc_admin.erl
|
3
|
+
%%% Author : Badlop <badlop@ono.com>
|
4
|
+
%%% Purpose : Tools for additional MUC administration
|
5
|
+
%%% Created : 8 Sep 2007 by Badlop <badlop@ono.com>
|
6
|
+
%%% Id : $Id$
|
7
|
+
%%%----------------------------------------------------------------------
|
8
|
+
|
9
|
+
-module(mod_muc_admin).
|
10
|
+
-author('badlop@ono.com').
|
11
|
+
|
12
|
+
-behaviour(gen_mod).
|
13
|
+
|
14
|
+
-export([
|
15
|
+
start/2, stop/1, % gen_mod API
|
16
|
+
muc_online_rooms/1,
|
17
|
+
muc_unregister_nick/1,
|
18
|
+
create_room/3, destroy_room/3,
|
19
|
+
create_rooms_file/1, destroy_rooms_file/1,
|
20
|
+
rooms_unused_list/2, rooms_unused_destroy/2,
|
21
|
+
get_room_occupants/2,
|
22
|
+
send_direct_invitation/4,
|
23
|
+
change_room_option/4,
|
24
|
+
set_room_affiliation/4,
|
25
|
+
get_room_affiliations/2,
|
26
|
+
web_menu_main/2, web_page_main/2, % Web Admin API
|
27
|
+
web_menu_host/3, web_page_host/3
|
28
|
+
]).
|
29
|
+
|
30
|
+
-include("ejabberd.hrl").
|
31
|
+
-include("jlib.hrl").
|
32
|
+
-include("mod_muc/mod_muc_room.hrl").
|
33
|
+
-include("web/ejabberd_http.hrl").
|
34
|
+
-include("web/ejabberd_web_admin.hrl").
|
35
|
+
-include("ejabberd_commands.hrl").
|
36
|
+
|
37
|
+
%% Copied from mod_muc/mod_muc.erl
|
38
|
+
-record(muc_online_room, {name_host, pid}).
|
39
|
+
|
40
|
+
%%----------------------------
|
41
|
+
%% gen_mod
|
42
|
+
%%----------------------------
|
43
|
+
|
44
|
+
start(Host, _Opts) ->
|
45
|
+
ejabberd_commands:register_commands(commands()),
|
46
|
+
ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50),
|
47
|
+
ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50),
|
48
|
+
ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50),
|
49
|
+
ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, web_page_host, 50).
|
50
|
+
|
51
|
+
stop(Host) ->
|
52
|
+
ejabberd_commands:unregister_commands(commands()),
|
53
|
+
ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50),
|
54
|
+
ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50),
|
55
|
+
ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50),
|
56
|
+
ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, web_page_host, 50),
|
57
|
+
ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, web_user, 50).
|
58
|
+
|
59
|
+
%%%
|
60
|
+
%%% Register commands
|
61
|
+
%%%
|
62
|
+
|
63
|
+
commands() ->
|
64
|
+
[
|
65
|
+
#ejabberd_commands{name = muc_online_rooms, tags = [muc],
|
66
|
+
desc = "List existing rooms ('global' to get all vhosts)",
|
67
|
+
module = ?MODULE, function = muc_online_rooms,
|
68
|
+
args = [{host, string}],
|
69
|
+
result = {rooms, {list, {room, string}}}},
|
70
|
+
#ejabberd_commands{name = muc_unregister_nick, tags = [muc],
|
71
|
+
desc = "Unregister the nick in the MUC service",
|
72
|
+
module = ?MODULE, function = muc_unregister_nick,
|
73
|
+
args = [{nick, string}],
|
74
|
+
result = {res, rescode}},
|
75
|
+
|
76
|
+
#ejabberd_commands{name = create_room, tags = [muc_room],
|
77
|
+
desc = "Create a MUC room name@service in host",
|
78
|
+
module = ?MODULE, function = create_room,
|
79
|
+
args = [{name, string}, {service, string},
|
80
|
+
{host, string}],
|
81
|
+
result = {res, rescode}},
|
82
|
+
#ejabberd_commands{name = destroy_room, tags = [muc_room],
|
83
|
+
desc = "Destroy a MUC room",
|
84
|
+
module = ?MODULE, function = destroy_room,
|
85
|
+
args = [{name, string}, {service, string},
|
86
|
+
{host, string}],
|
87
|
+
result = {res, rescode}},
|
88
|
+
#ejabberd_commands{name = create_rooms_file, tags = [muc],
|
89
|
+
desc = "Create the rooms indicated in file",
|
90
|
+
module = ?MODULE, function = create_rooms_file,
|
91
|
+
args = [{file, string}],
|
92
|
+
result = {res, rescode}},
|
93
|
+
#ejabberd_commands{name = destroy_rooms_file, tags = [muc],
|
94
|
+
desc = "Destroy the rooms indicated in file",
|
95
|
+
module = ?MODULE, function = destroy_rooms_file,
|
96
|
+
args = [{file, string}],
|
97
|
+
result = {res, rescode}},
|
98
|
+
#ejabberd_commands{name = rooms_unused_list, tags = [muc],
|
99
|
+
desc = "List the rooms that are unused for many days in host",
|
100
|
+
module = ?MODULE, function = rooms_unused_list,
|
101
|
+
args = [{host, string}, {days, integer}],
|
102
|
+
result = {rooms, {list, {room, string}}}},
|
103
|
+
#ejabberd_commands{name = rooms_unused_destroy, tags = [muc],
|
104
|
+
desc = "Destroy the rooms that are unused for many days in host",
|
105
|
+
module = ?MODULE, function = rooms_unused_destroy,
|
106
|
+
args = [{host, string}, {days, integer}],
|
107
|
+
result = {rooms, {list, {room, string}}}},
|
108
|
+
|
109
|
+
#ejabberd_commands{name = get_room_occupants, tags = [muc_room],
|
110
|
+
desc = "Get the list of occupants of a MUC room",
|
111
|
+
module = ?MODULE, function = get_room_occupants,
|
112
|
+
args = [{name, string}, {service, string}],
|
113
|
+
result = {occupants, {list,
|
114
|
+
{occupant, {tuple,
|
115
|
+
[{jid, string},
|
116
|
+
{nick, string},
|
117
|
+
{role, string}
|
118
|
+
]}}
|
119
|
+
}}},
|
120
|
+
|
121
|
+
#ejabberd_commands{name = send_direct_invitation, tags = [muc_room],
|
122
|
+
desc = "Send a direct invitation to several destinations",
|
123
|
+
longdesc = "Password and Message can also be: none. Users JIDs are separated with : ",
|
124
|
+
module = ?MODULE, function = send_direct_invitation,
|
125
|
+
args = [{room, string}, {password, string}, {reason, string}, {users, string}],
|
126
|
+
result = {res, rescode}},
|
127
|
+
|
128
|
+
#ejabberd_commands{name = change_room_option, tags = [muc_room],
|
129
|
+
desc = "Change an option in a MUC room",
|
130
|
+
module = ?MODULE, function = change_room_option,
|
131
|
+
args = [{name, string}, {service, string},
|
132
|
+
{option, string}, {value, string}],
|
133
|
+
result = {res, rescode}},
|
134
|
+
|
135
|
+
#ejabberd_commands{name = set_room_affiliation, tags = [muc_room],
|
136
|
+
desc = "Change an affiliation in a MUC room",
|
137
|
+
module = ?MODULE, function = set_room_affiliation,
|
138
|
+
args = [{name, string}, {service, string},
|
139
|
+
{jid, string}, {affiliation, string}],
|
140
|
+
result = {res, rescode}},
|
141
|
+
#ejabberd_commands{name = get_room_affiliations, tags = [muc_room],
|
142
|
+
desc = "Get the list of affiliations of a MUC room",
|
143
|
+
module = ?MODULE, function = get_room_affiliations,
|
144
|
+
args = [{name, string}, {service, string}],
|
145
|
+
result = {affiliations, {list,
|
146
|
+
{affiliation, {tuple,
|
147
|
+
[{username, string},
|
148
|
+
{domain, string},
|
149
|
+
{affiliation, atom},
|
150
|
+
{reason, string}
|
151
|
+
]}}
|
152
|
+
}}}
|
153
|
+
].
|
154
|
+
|
155
|
+
|
156
|
+
%%%
|
157
|
+
%%% ejabberd commands
|
158
|
+
%%%
|
159
|
+
|
160
|
+
muc_online_rooms(ServerHost) ->
|
161
|
+
MUCHost = find_host(ServerHost),
|
162
|
+
Rooms = ets:tab2list(muc_online_room),
|
163
|
+
lists:foldl(
|
164
|
+
fun({_, {Roomname, Host}, _}, Results) ->
|
165
|
+
case MUCHost of
|
166
|
+
global ->
|
167
|
+
[Roomname ++ "@" ++ Host | Results];
|
168
|
+
Host ->
|
169
|
+
[Roomname ++ "@" ++ Host | Results];
|
170
|
+
_ ->
|
171
|
+
Results
|
172
|
+
end
|
173
|
+
end,
|
174
|
+
[],
|
175
|
+
Rooms).
|
176
|
+
|
177
|
+
muc_unregister_nick(Nick) ->
|
178
|
+
F2 = fun(N) ->
|
179
|
+
[{_,Key,_}] = mnesia:index_read(muc_registered, N, 3),
|
180
|
+
mnesia:delete({muc_registered, Key})
|
181
|
+
end,
|
182
|
+
case mnesia:transaction(F2, [Nick], 1) of
|
183
|
+
{atomic, ok} ->
|
184
|
+
ok;
|
185
|
+
{aborted, _Error} ->
|
186
|
+
error
|
187
|
+
end.
|
188
|
+
|
189
|
+
|
190
|
+
%%----------------------------
|
191
|
+
%% Ad-hoc commands
|
192
|
+
%%----------------------------
|
193
|
+
|
194
|
+
|
195
|
+
%%----------------------------
|
196
|
+
%% Web Admin
|
197
|
+
%%----------------------------
|
198
|
+
|
199
|
+
%%---------------
|
200
|
+
%% Web Admin Menu
|
201
|
+
|
202
|
+
web_menu_main(Acc, Lang) ->
|
203
|
+
Acc ++ [{"muc", ?T("Multi-User Chat")}].
|
204
|
+
|
205
|
+
web_menu_host(Acc, _Host, Lang) ->
|
206
|
+
Acc ++ [{"muc", ?T("Multi-User Chat")}].
|
207
|
+
|
208
|
+
|
209
|
+
%%---------------
|
210
|
+
%% Web Admin Page
|
211
|
+
|
212
|
+
-define(TDTD(L, N),
|
213
|
+
?XE("tr", [?XCT("td", L),
|
214
|
+
?XC("td", integer_to_list(N))
|
215
|
+
])).
|
216
|
+
|
217
|
+
web_page_main(_, #request{path=["muc"], lang = Lang} = _Request) ->
|
218
|
+
Res = [?XC("h1", "Multi-User Chat"),
|
219
|
+
?XC("h3", "Statistics"),
|
220
|
+
?XAE("table", [],
|
221
|
+
[?XE("tbody", [?TDTD("Total rooms", ets:info(muc_online_room, size)),
|
222
|
+
?TDTD("Permanent rooms", mnesia:table_info(muc_room, size)),
|
223
|
+
?TDTD("Registered nicknames", mnesia:table_info(muc_registered, size))
|
224
|
+
])
|
225
|
+
]),
|
226
|
+
?XE("ul", [?LI([?ACT("rooms", "List of rooms")])])
|
227
|
+
],
|
228
|
+
{stop, Res};
|
229
|
+
|
230
|
+
web_page_main(_, #request{path=["muc", "rooms"], q = Q, lang = Lang} = _Request) ->
|
231
|
+
Sort_query = get_sort_query(Q),
|
232
|
+
Res = make_rooms_page(global, Lang, Sort_query),
|
233
|
+
{stop, Res};
|
234
|
+
|
235
|
+
web_page_main(Acc, _) -> Acc.
|
236
|
+
|
237
|
+
web_page_host(_, Host,
|
238
|
+
#request{path = ["muc"],
|
239
|
+
q = Q,
|
240
|
+
lang = Lang} = _Request) ->
|
241
|
+
Sort_query = get_sort_query(Q),
|
242
|
+
Res = make_rooms_page(find_host(Host), Lang, Sort_query),
|
243
|
+
{stop, Res};
|
244
|
+
web_page_host(Acc, _, _) -> Acc.
|
245
|
+
|
246
|
+
|
247
|
+
%% Returns: {normal | reverse, Integer}
|
248
|
+
get_sort_query(Q) ->
|
249
|
+
case catch get_sort_query2(Q) of
|
250
|
+
{ok, Res} -> Res;
|
251
|
+
_ -> {normal, 1}
|
252
|
+
end.
|
253
|
+
|
254
|
+
get_sort_query2(Q) ->
|
255
|
+
{value, {_, String}} = lists:keysearch("sort", 1, Q),
|
256
|
+
Integer = list_to_integer(String),
|
257
|
+
case Integer >= 0 of
|
258
|
+
true -> {ok, {normal, Integer}};
|
259
|
+
false -> {ok, {reverse, abs(Integer)}}
|
260
|
+
end.
|
261
|
+
|
262
|
+
make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) ->
|
263
|
+
Rooms_names = get_rooms(Host),
|
264
|
+
Rooms_infos = build_info_rooms(Rooms_names),
|
265
|
+
Rooms_sorted = sort_rooms(Sort_direction, Sort_column, Rooms_infos),
|
266
|
+
Rooms_prepared = prepare_rooms_infos(Rooms_sorted),
|
267
|
+
TList = lists:map(
|
268
|
+
fun(Room) ->
|
269
|
+
?XE("tr", [?XC("td", E) || E <- Room])
|
270
|
+
end, Rooms_prepared),
|
271
|
+
Titles = ["Jabber ID",
|
272
|
+
"# participants",
|
273
|
+
"Last message",
|
274
|
+
"Public",
|
275
|
+
"Persistent",
|
276
|
+
"Logging",
|
277
|
+
"Just created",
|
278
|
+
"Title"],
|
279
|
+
{Titles_TR, _} =
|
280
|
+
lists:mapfoldl(
|
281
|
+
fun(Title, Num_column) ->
|
282
|
+
NCS = integer_to_list(Num_column),
|
283
|
+
TD = ?XE("td", [?CT(Title),
|
284
|
+
?C(" "),
|
285
|
+
?ACT("?sort="++NCS, "<"),
|
286
|
+
?C(" "),
|
287
|
+
?ACT("?sort=-"++NCS, ">")]),
|
288
|
+
{TD, Num_column+1}
|
289
|
+
end,
|
290
|
+
1,
|
291
|
+
Titles),
|
292
|
+
[?XC("h1", "Multi-User Chat"),
|
293
|
+
?XC("h2", "Rooms"),
|
294
|
+
?XE("table",
|
295
|
+
[?XE("thead",
|
296
|
+
[?XE("tr", Titles_TR)]
|
297
|
+
),
|
298
|
+
?XE("tbody", TList)
|
299
|
+
]
|
300
|
+
)
|
301
|
+
].
|
302
|
+
|
303
|
+
sort_rooms(Direction, Column, Rooms) ->
|
304
|
+
Rooms2 = lists:keysort(Column, Rooms),
|
305
|
+
case Direction of
|
306
|
+
normal -> Rooms2;
|
307
|
+
reverse -> lists:reverse(Rooms2)
|
308
|
+
end.
|
309
|
+
|
310
|
+
build_info_rooms(Rooms) ->
|
311
|
+
[build_info_room(Room) || Room <- Rooms].
|
312
|
+
|
313
|
+
build_info_room({Name, Host, Pid}) ->
|
314
|
+
C = get_room_config(Pid),
|
315
|
+
Title = C#config.title,
|
316
|
+
Public = C#config.public,
|
317
|
+
Persistent = C#config.persistent,
|
318
|
+
Logging = C#config.logging,
|
319
|
+
|
320
|
+
S = get_room_state(Pid),
|
321
|
+
Just_created = S#state.just_created,
|
322
|
+
Num_participants = length(dict:fetch_keys(S#state.users)),
|
323
|
+
|
324
|
+
History = (S#state.history)#lqueue.queue,
|
325
|
+
Ts_last_message =
|
326
|
+
case queue:is_empty(History) of
|
327
|
+
true ->
|
328
|
+
"A long time ago";
|
329
|
+
false ->
|
330
|
+
Last_message1 = queue:last(History),
|
331
|
+
{_, _, _, Ts_last, _} = Last_message1,
|
332
|
+
jlib:timestamp_to_iso(Ts_last)
|
333
|
+
end,
|
334
|
+
|
335
|
+
{Name++"@"++Host,
|
336
|
+
Num_participants,
|
337
|
+
Ts_last_message,
|
338
|
+
Public,
|
339
|
+
Persistent,
|
340
|
+
Logging,
|
341
|
+
Just_created,
|
342
|
+
Title}.
|
343
|
+
|
344
|
+
prepare_rooms_infos(Rooms) ->
|
345
|
+
[prepare_room_info(Room) || Room <- Rooms].
|
346
|
+
prepare_room_info(Room_info) ->
|
347
|
+
{NameHost,
|
348
|
+
Num_participants,
|
349
|
+
Ts_last_message,
|
350
|
+
Public,
|
351
|
+
Persistent,
|
352
|
+
Logging,
|
353
|
+
Just_created,
|
354
|
+
Title} = Room_info,
|
355
|
+
[NameHost,
|
356
|
+
integer_to_list(Num_participants),
|
357
|
+
Ts_last_message,
|
358
|
+
atom_to_list(Public),
|
359
|
+
atom_to_list(Persistent),
|
360
|
+
atom_to_list(Logging),
|
361
|
+
atom_to_list(Just_created),
|
362
|
+
Title].
|
363
|
+
|
364
|
+
|
365
|
+
%%----------------------------
|
366
|
+
%% Create/Delete Room
|
367
|
+
%%----------------------------
|
368
|
+
|
369
|
+
%% @spec (Name::string(), Host::string(), ServerHost::string()) ->
|
370
|
+
%% ok | error
|
371
|
+
%% @doc Create a room immediately with the default options.
|
372
|
+
create_room(Name, Host, ServerHost) ->
|
373
|
+
|
374
|
+
%% Get the default room options from the muc configuration
|
375
|
+
DefRoomOpts = gen_mod:get_module_opt(ServerHost, mod_muc,
|
376
|
+
default_room_options, []),
|
377
|
+
|
378
|
+
%% Store the room on the server, it is not started yet though at this point
|
379
|
+
mod_muc:store_room(Host, Name, DefRoomOpts),
|
380
|
+
|
381
|
+
%% Get all remaining mod_muc parameters that might be utilized
|
382
|
+
Access = gen_mod:get_module_opt(ServerHost, mod_muc, access, all),
|
383
|
+
AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create, all),
|
384
|
+
AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin, none),
|
385
|
+
AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent, all),
|
386
|
+
HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, 20),
|
387
|
+
RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper, none),
|
388
|
+
|
389
|
+
%% If the room does not exist yet in the muc_online_room
|
390
|
+
case mnesia:dirty_read(muc_online_room, {Name, Host}) of
|
391
|
+
[] ->
|
392
|
+
%% Start the room
|
393
|
+
{ok, Pid} = mod_muc_room:start(
|
394
|
+
Host,
|
395
|
+
ServerHost,
|
396
|
+
{Access, AcCreate, AcAdmin, AcPer},
|
397
|
+
Name,
|
398
|
+
HistorySize,
|
399
|
+
RoomShaper,
|
400
|
+
DefRoomOpts),
|
401
|
+
{atomic, ok} = register_room(Host, Name, Pid),
|
402
|
+
ok;
|
403
|
+
_ ->
|
404
|
+
error
|
405
|
+
end.
|
406
|
+
|
407
|
+
register_room(Host, Name, Pid) ->
|
408
|
+
F = fun() ->
|
409
|
+
mnesia:write(#muc_online_room{name_host = {Name, Host},
|
410
|
+
pid = Pid})
|
411
|
+
end,
|
412
|
+
mnesia:transaction(F).
|
413
|
+
|
414
|
+
%% Create the room only in the database.
|
415
|
+
%% It is required to restart the MUC service for the room to appear.
|
416
|
+
muc_create_room({Name, Host, _}, DefRoomOpts) ->
|
417
|
+
io:format("Creating room ~s@~s~n", [Name, Host]),
|
418
|
+
mod_muc:store_room(Host, Name, DefRoomOpts).
|
419
|
+
|
420
|
+
%% @spec (Name::string(), Host::string(), ServerHost::string()) ->
|
421
|
+
%% ok | {error, room_not_exists}
|
422
|
+
%% @doc Destroy the room immediately.
|
423
|
+
%% If the room has participants, they are not notified that the room was destroyed;
|
424
|
+
%% they will notice when they try to chat and receive an error that the room doesn't exist.
|
425
|
+
destroy_room(Name, Service, _Server) ->
|
426
|
+
case mnesia:dirty_read(muc_online_room, {Name, Service}) of
|
427
|
+
[R] ->
|
428
|
+
Pid = R#muc_online_room.pid,
|
429
|
+
gen_fsm:send_all_state_event(Pid, destroy),
|
430
|
+
ok;
|
431
|
+
[] ->
|
432
|
+
error
|
433
|
+
end.
|
434
|
+
|
435
|
+
destroy_room({N, H, SH}) ->
|
436
|
+
io:format("Destroying room: ~s@~s - vhost: ~s~n", [N, H, SH]),
|
437
|
+
destroy_room(N, H, SH).
|
438
|
+
|
439
|
+
|
440
|
+
%%----------------------------
|
441
|
+
%% Destroy Rooms in File
|
442
|
+
%%----------------------------
|
443
|
+
|
444
|
+
%% The format of the file is: one chatroom JID per line
|
445
|
+
%% The file encoding must be UTF-8
|
446
|
+
|
447
|
+
destroy_rooms_file(Filename) ->
|
448
|
+
{ok, F} = file:open(Filename, [read]),
|
449
|
+
RJID = read_room(F),
|
450
|
+
Rooms = read_rooms(F, RJID, []),
|
451
|
+
file:close(F),
|
452
|
+
[destroy_room(A) || A <- Rooms],
|
453
|
+
ok.
|
454
|
+
|
455
|
+
read_rooms(_F, eof, L) ->
|
456
|
+
L;
|
457
|
+
|
458
|
+
read_rooms(F, RJID, L) ->
|
459
|
+
RJID2 = read_room(F),
|
460
|
+
read_rooms(F, RJID2, [RJID | L]).
|
461
|
+
|
462
|
+
read_room(F) ->
|
463
|
+
case io:get_line(F, "") of
|
464
|
+
eof -> eof;
|
465
|
+
String ->
|
466
|
+
case io_lib:fread("~s", String) of
|
467
|
+
{ok, [RoomJID], _} -> split_roomjid(RoomJID);
|
468
|
+
{error, What} ->
|
469
|
+
io:format("Parse error: what: ~p~non the line: ~p~n~n", [What, String])
|
470
|
+
end
|
471
|
+
end.
|
472
|
+
|
473
|
+
%% This function is quite rudimentary
|
474
|
+
%% and may not be accurate
|
475
|
+
split_roomjid(RoomJID) ->
|
476
|
+
[Name, Host] = string:tokens(RoomJID, "@"),
|
477
|
+
[_MUC_service_name | ServerHostList] = string:tokens(Host, "."),
|
478
|
+
ServerHost = join(ServerHostList, "."),
|
479
|
+
{Name, Host, ServerHost}.
|
480
|
+
|
481
|
+
%% This function is copied from string:join/2 in Erlang/OTP R12B-1
|
482
|
+
%% Note that string:join/2 is not implemented in Erlang/OTP R11B
|
483
|
+
join([H|T], Sep) ->
|
484
|
+
H ++ lists:concat([Sep ++ X || X <- T]).
|
485
|
+
|
486
|
+
|
487
|
+
%%----------------------------
|
488
|
+
%% Create Rooms in File
|
489
|
+
%%----------------------------
|
490
|
+
|
491
|
+
create_rooms_file(Filename) ->
|
492
|
+
{ok, F} = file:open(Filename, [read]),
|
493
|
+
RJID = read_room(F),
|
494
|
+
Rooms = read_rooms(F, RJID, []),
|
495
|
+
file:close(F),
|
496
|
+
%% Read the default room options defined for the first virtual host
|
497
|
+
DefRoomOpts = gen_mod:get_module_opt(?MYNAME, mod_muc,
|
498
|
+
default_room_options, []),
|
499
|
+
[muc_create_room(A, DefRoomOpts) || A <- Rooms],
|
500
|
+
ok.
|
501
|
+
|
502
|
+
|
503
|
+
%%----------------------------
|
504
|
+
%% List/Delete Unused Rooms
|
505
|
+
%%----------------------------
|
506
|
+
|
507
|
+
%%---------------
|
508
|
+
%% Control
|
509
|
+
|
510
|
+
rooms_unused_list(Host, Days) ->
|
511
|
+
rooms_unused_report(list, Host, Days).
|
512
|
+
rooms_unused_destroy(Host, Days) ->
|
513
|
+
rooms_unused_report(destroy, Host, Days).
|
514
|
+
|
515
|
+
rooms_unused_report(Action, Host, Days) ->
|
516
|
+
{NA, NP, RP} = muc_unused(Action, Host, Days),
|
517
|
+
io:format("Unused rooms: ~p out of ~p~n", [NP, NA]),
|
518
|
+
[R ++ "@" ++ H || {R, H, _P} <- RP].
|
519
|
+
|
520
|
+
muc_unused(Action, ServerHost, Days) ->
|
521
|
+
Host = find_host(ServerHost),
|
522
|
+
muc_unused2(Action, ServerHost, Host, Days).
|
523
|
+
|
524
|
+
muc_unused2(Action, ServerHost, Host, Last_allowed) ->
|
525
|
+
%% Get all required info about all existing rooms
|
526
|
+
Rooms_all = get_rooms(Host),
|
527
|
+
|
528
|
+
%% Decide which ones pass the requirements
|
529
|
+
Rooms_pass = decide_rooms(Rooms_all, Last_allowed),
|
530
|
+
|
531
|
+
Num_rooms_all = length(Rooms_all),
|
532
|
+
Num_rooms_pass = length(Rooms_pass),
|
533
|
+
|
534
|
+
%% Perform the desired action for matching rooms
|
535
|
+
act_on_rooms(Action, Rooms_pass, ServerHost),
|
536
|
+
|
537
|
+
{Num_rooms_all, Num_rooms_pass, Rooms_pass}.
|
538
|
+
|
539
|
+
%%---------------
|
540
|
+
%% Get info
|
541
|
+
|
542
|
+
get_rooms(Host) ->
|
543
|
+
Get_room_names = fun(Room_reg, Names) ->
|
544
|
+
Pid = Room_reg#muc_online_room.pid,
|
545
|
+
case {Host, Room_reg#muc_online_room.name_host} of
|
546
|
+
{Host, {Name1, Host}} ->
|
547
|
+
[{Name1, Host, Pid} | Names];
|
548
|
+
{global, {Name1, Host1}} ->
|
549
|
+
[{Name1, Host1, Pid} | Names];
|
550
|
+
_ ->
|
551
|
+
Names
|
552
|
+
end
|
553
|
+
end,
|
554
|
+
ets:foldr(Get_room_names, [], muc_online_room).
|
555
|
+
|
556
|
+
get_room_config(Room_pid) ->
|
557
|
+
{ok, R} = gen_fsm:sync_send_all_state_event(Room_pid, get_config),
|
558
|
+
R.
|
559
|
+
|
560
|
+
get_room_state(Room_pid) ->
|
561
|
+
{ok, R} = gen_fsm:sync_send_all_state_event(Room_pid, get_state),
|
562
|
+
R.
|
563
|
+
|
564
|
+
%%---------------
|
565
|
+
%% Decide
|
566
|
+
|
567
|
+
decide_rooms(Rooms, Last_allowed) ->
|
568
|
+
Decide = fun(R) -> decide_room(R, Last_allowed) end,
|
569
|
+
lists:filter(Decide, Rooms).
|
570
|
+
|
571
|
+
decide_room({_Room_name, _Host, Room_pid}, Last_allowed) ->
|
572
|
+
C = get_room_config(Room_pid),
|
573
|
+
Persistent = C#config.persistent,
|
574
|
+
|
575
|
+
S = get_room_state(Room_pid),
|
576
|
+
Just_created = S#state.just_created,
|
577
|
+
|
578
|
+
Room_users = S#state.users,
|
579
|
+
Num_users = length(?DICT:to_list(Room_users)),
|
580
|
+
|
581
|
+
History = (S#state.history)#lqueue.queue,
|
582
|
+
Ts_now = calendar:now_to_universal_time(now()),
|
583
|
+
Ts_uptime = uptime_seconds(),
|
584
|
+
{Has_hist, Last} = case queue:is_empty(History) of
|
585
|
+
true ->
|
586
|
+
{false, Ts_uptime};
|
587
|
+
false ->
|
588
|
+
Last_message = queue:last(History),
|
589
|
+
{_, _, _, Ts_last, _} = Last_message,
|
590
|
+
Ts_diff =
|
591
|
+
calendar:datetime_to_gregorian_seconds(Ts_now)
|
592
|
+
- calendar:datetime_to_gregorian_seconds(Ts_last),
|
593
|
+
{true, Ts_diff}
|
594
|
+
end,
|
595
|
+
|
596
|
+
case {Persistent, Just_created, Num_users, Has_hist, seconds_to_days(Last)} of
|
597
|
+
{_true, false, 0, _, Last_days}
|
598
|
+
when Last_days >= Last_allowed ->
|
599
|
+
true;
|
600
|
+
_ ->
|
601
|
+
false
|
602
|
+
end.
|
603
|
+
|
604
|
+
seconds_to_days(S) ->
|
605
|
+
S div (60*60*24).
|
606
|
+
|
607
|
+
%%---------------
|
608
|
+
%% Act
|
609
|
+
|
610
|
+
act_on_rooms(Action, Rooms, ServerHost) ->
|
611
|
+
ServerHosts = [ {A, find_host(A)} || A <- ?MYHOSTS ],
|
612
|
+
Delete = fun({_N, H, _Pid} = Room) ->
|
613
|
+
SH = case ServerHost of
|
614
|
+
global -> find_serverhost(H, ServerHosts);
|
615
|
+
O -> O
|
616
|
+
end,
|
617
|
+
|
618
|
+
act_on_room(Action, Room, SH)
|
619
|
+
end,
|
620
|
+
lists:foreach(Delete, Rooms).
|
621
|
+
|
622
|
+
find_serverhost(Host, ServerHosts) ->
|
623
|
+
{value, {ServerHost, Host}} = lists:keysearch(Host, 2, ServerHosts),
|
624
|
+
ServerHost.
|
625
|
+
|
626
|
+
act_on_room(destroy, {N, H, Pid}, SH) ->
|
627
|
+
gen_fsm:send_all_state_event(
|
628
|
+
Pid, {destroy, "Room destroyed by rooms_unused_destroy."}),
|
629
|
+
mod_muc:room_destroyed(H, N, Pid, SH),
|
630
|
+
mod_muc:forget_room(H, N);
|
631
|
+
|
632
|
+
act_on_room(list, _, _) ->
|
633
|
+
ok.
|
634
|
+
|
635
|
+
|
636
|
+
%%----------------------------
|
637
|
+
%% Change Room Option
|
638
|
+
%%----------------------------
|
639
|
+
|
640
|
+
get_room_occupants(Room, Host) ->
|
641
|
+
case get_room_pid(Room, Host) of
|
642
|
+
room_not_found -> throw({error, room_not_found});
|
643
|
+
Pid -> get_room_occupants(Pid)
|
644
|
+
end.
|
645
|
+
|
646
|
+
get_room_occupants(Pid) ->
|
647
|
+
S = get_room_state(Pid),
|
648
|
+
lists:map(
|
649
|
+
fun({_LJID, Info}) ->
|
650
|
+
{jlib:jid_to_string(Info#user.jid),
|
651
|
+
Info#user.nick,
|
652
|
+
atom_to_list(Info#user.role)}
|
653
|
+
end,
|
654
|
+
dict:to_list(S#state.users)).
|
655
|
+
|
656
|
+
%%----------------------------
|
657
|
+
%% Send Direct Invitation
|
658
|
+
%%----------------------------
|
659
|
+
%% http://xmpp.org/extensions/xep-0249.html
|
660
|
+
|
661
|
+
send_direct_invitation(RoomString, Password, Reason, UsersString) ->
|
662
|
+
RoomJid = jlib:string_to_jid(RoomString),
|
663
|
+
XmlEl = build_invitation(Password, Reason, RoomString),
|
664
|
+
UsersStrings = get_users_to_invite(RoomJid, UsersString),
|
665
|
+
[send_direct_invitation(RoomJid, jlib:string_to_jid(UserStrings), XmlEl)
|
666
|
+
|| UserStrings <- UsersStrings],
|
667
|
+
timer:sleep(1000),
|
668
|
+
ok.
|
669
|
+
|
670
|
+
get_users_to_invite(RoomJid, UsersString) ->
|
671
|
+
UsersStrings = string:tokens(UsersString, ":"),
|
672
|
+
OccupantsTuples = get_room_occupants(RoomJid#jid.luser,
|
673
|
+
RoomJid#jid.lserver),
|
674
|
+
OccupantsJids = [jlib:string_to_jid(JidString)
|
675
|
+
|| {JidString, _Nick, _} <- OccupantsTuples],
|
676
|
+
lists:filter(
|
677
|
+
fun(UserString) ->
|
678
|
+
UserJid = jlib:string_to_jid(UserString),
|
679
|
+
%% [{"badlop@localhost/work","badlop","moderator"}]
|
680
|
+
lists:all(fun(OccupantJid) ->
|
681
|
+
UserJid#jid.luser /= OccupantJid#jid.luser
|
682
|
+
orelse UserJid#jid.lserver /= OccupantJid#jid.lserver
|
683
|
+
end,
|
684
|
+
OccupantsJids)
|
685
|
+
end,
|
686
|
+
UsersStrings).
|
687
|
+
|
688
|
+
build_invitation(Password, Reason, RoomString) ->
|
689
|
+
PasswordAttrList = case Password of
|
690
|
+
"none" -> [];
|
691
|
+
_ -> [{"password", Password}]
|
692
|
+
end,
|
693
|
+
ReasonAttrList = case Reason of
|
694
|
+
"none" -> [];
|
695
|
+
_ -> [{"reason", Reason}]
|
696
|
+
end,
|
697
|
+
XAttrs = [{"xmlns", ?NS_XCONFERENCE},
|
698
|
+
{"jid", RoomString}]
|
699
|
+
++ PasswordAttrList
|
700
|
+
++ ReasonAttrList,
|
701
|
+
XEl = {xmlelement, "x", XAttrs, []},
|
702
|
+
{xmlelement, "message", [], [XEl]}.
|
703
|
+
|
704
|
+
send_direct_invitation(FromJid, UserJid, XmlEl) ->
|
705
|
+
ejabberd_router:route(FromJid, UserJid, XmlEl).
|
706
|
+
|
707
|
+
%%----------------------------
|
708
|
+
%% Change Room Option
|
709
|
+
%%----------------------------
|
710
|
+
|
711
|
+
%% @spec(Name::string(), Service::string(), Option::string(), Value) -> ok
|
712
|
+
%% Value = atom() | integer() | string()
|
713
|
+
%% @doc Change an option in an existing room.
|
714
|
+
%% Requires the name of the room, the MUC service where it exists,
|
715
|
+
%% the option to change (for example title or max_users),
|
716
|
+
%% and the value to assign to the new option.
|
717
|
+
%% For example:
|
718
|
+
%% change_room_option("testroom", "conference.localhost", "title", "Test Room")
|
719
|
+
change_room_option(Name, Service, Option, Value) when is_atom(Option) ->
|
720
|
+
Pid = get_room_pid(Name, Service),
|
721
|
+
{ok, _} = change_room_option(Pid, Option, Value),
|
722
|
+
ok;
|
723
|
+
change_room_option(Name, Service, OptionString, ValueString) ->
|
724
|
+
Option = list_to_atom(OptionString),
|
725
|
+
Value = case Option of
|
726
|
+
title -> ValueString;
|
727
|
+
description -> ValueString;
|
728
|
+
password -> ValueString;
|
729
|
+
max_users -> list_to_integer(ValueString);
|
730
|
+
_ -> list_to_atom(ValueString)
|
731
|
+
end,
|
732
|
+
change_room_option(Name, Service, Option, Value).
|
733
|
+
|
734
|
+
change_room_option(Pid, Option, Value) ->
|
735
|
+
Config = get_room_config(Pid),
|
736
|
+
Config2 = change_option(Option, Value, Config),
|
737
|
+
gen_fsm:sync_send_all_state_event(Pid, {change_config, Config2}).
|
738
|
+
|
739
|
+
%% @doc Get the Pid of an existing MUC room, or 'room_not_found'.
|
740
|
+
get_room_pid(Name, Service) ->
|
741
|
+
case mnesia:dirty_read(muc_online_room, {Name, Service}) of
|
742
|
+
[] ->
|
743
|
+
room_not_found;
|
744
|
+
[Room] ->
|
745
|
+
Room#muc_online_room.pid
|
746
|
+
end.
|
747
|
+
|
748
|
+
%% It is required to put explicitely all the options because
|
749
|
+
%% the record elements are replaced at compile time.
|
750
|
+
%% So, this can't be parametrized.
|
751
|
+
change_option(Option, Value, Config) ->
|
752
|
+
case Option of
|
753
|
+
allow_change_subj -> Config#config{allow_change_subj = Value};
|
754
|
+
allow_private_messages -> Config#config{allow_private_messages = Value};
|
755
|
+
allow_query_users -> Config#config{allow_query_users = Value};
|
756
|
+
allow_user_invites -> Config#config{allow_user_invites = Value};
|
757
|
+
anonymous -> Config#config{anonymous = Value};
|
758
|
+
logging -> Config#config{logging = Value};
|
759
|
+
max_users -> Config#config{max_users = Value};
|
760
|
+
members_by_default -> Config#config{members_by_default = Value};
|
761
|
+
members_only -> Config#config{members_only = Value};
|
762
|
+
moderated -> Config#config{moderated = Value};
|
763
|
+
password -> Config#config{password = Value};
|
764
|
+
password_protected -> Config#config{password_protected = Value};
|
765
|
+
persistent -> Config#config{persistent = Value};
|
766
|
+
public -> Config#config{public = Value};
|
767
|
+
public_list -> Config#config{public_list = Value};
|
768
|
+
title -> Config#config{title = Value}
|
769
|
+
end.
|
770
|
+
|
771
|
+
|
772
|
+
%%----------------------------
|
773
|
+
%% Get Room Affiliations
|
774
|
+
%%----------------------------
|
775
|
+
|
776
|
+
%% @spec(Name::string(), Service::string()) ->
|
777
|
+
%% [{JID::string(), Domain::string(), Role::string(), Reason::string()}]
|
778
|
+
%% @doc Get the affiliations of the room Name@Service.
|
779
|
+
get_room_affiliations(Name, Service) ->
|
780
|
+
case mnesia:dirty_read(muc_online_room, {Name, Service}) of
|
781
|
+
[R] ->
|
782
|
+
%% Get the PID of the online room, then request its state
|
783
|
+
Pid = R#muc_online_room.pid,
|
784
|
+
{ok, StateData} = gen_fsm:sync_send_all_state_event(Pid, get_state),
|
785
|
+
Affiliations = ?DICT:to_list(StateData#state.affiliations),
|
786
|
+
lists:map(
|
787
|
+
fun({{Uname, Domain, _Res}, {Aff, Reason}}) when is_atom(Aff)->
|
788
|
+
{Uname, Domain, Aff, Reason};
|
789
|
+
({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)->
|
790
|
+
{Uname, Domain, Aff, ""}
|
791
|
+
end, Affiliations);
|
792
|
+
[] ->
|
793
|
+
throw({error, "The room does not exist."})
|
794
|
+
end.
|
795
|
+
|
796
|
+
%%----------------------------
|
797
|
+
%% Change Room Affiliation
|
798
|
+
%%----------------------------
|
799
|
+
|
800
|
+
%% @spec(Name, Service, JID, AffiliationString) -> ok | {error, Error}
|
801
|
+
%% Name = string()
|
802
|
+
%% Service = string()
|
803
|
+
%% JID = string()
|
804
|
+
%% AffiliationString = "outcast" | "none" | "member" | "admin" | "owner"
|
805
|
+
%% @doc Set the affiliation of JID in the room Name@Service.
|
806
|
+
%% If the affiliation is 'none', the action is to remove,
|
807
|
+
%% In any other case the action will be to create the affiliation.
|
808
|
+
set_room_affiliation(Name, Service, JID, AffiliationString) ->
|
809
|
+
Affiliation = list_to_atom(AffiliationString),
|
810
|
+
case mnesia:dirty_read(muc_online_room, {Name, Service}) of
|
811
|
+
[R] ->
|
812
|
+
%% Get the PID for the online room so we can get the state of the room
|
813
|
+
Pid = R#muc_online_room.pid,
|
814
|
+
{ok, StateData} = gen_fsm:sync_send_all_state_event(Pid, get_state),
|
815
|
+
SJID = jlib:string_to_jid(JID),
|
816
|
+
LJID = jlib:jid_remove_resource(jlib:jid_tolower(SJID)),
|
817
|
+
Affiliations = change_affiliation(Affiliation, LJID, StateData#state.affiliations),
|
818
|
+
Res = StateData#state{affiliations = Affiliations},
|
819
|
+
{ok, _State} = gen_fsm:sync_send_all_state_event(Pid, {change_state, Res}),
|
820
|
+
mod_muc:store_room(Res#state.host, Res#state.room, make_opts(Res)),
|
821
|
+
ok;
|
822
|
+
[] ->
|
823
|
+
error
|
824
|
+
end.
|
825
|
+
|
826
|
+
change_affiliation(none, LJID, Affiliations) ->
|
827
|
+
?DICT:erase(LJID, Affiliations);
|
828
|
+
change_affiliation(Affiliation, LJID, Affiliations) ->
|
829
|
+
?DICT:store(LJID, Affiliation, Affiliations).
|
830
|
+
|
831
|
+
-define(MAKE_CONFIG_OPT(Opt), {Opt, Config#config.Opt}).
|
832
|
+
|
833
|
+
make_opts(StateData) ->
|
834
|
+
Config = StateData#state.config,
|
835
|
+
[
|
836
|
+
?MAKE_CONFIG_OPT(title),
|
837
|
+
?MAKE_CONFIG_OPT(allow_change_subj),
|
838
|
+
?MAKE_CONFIG_OPT(allow_query_users),
|
839
|
+
?MAKE_CONFIG_OPT(allow_private_messages),
|
840
|
+
?MAKE_CONFIG_OPT(public),
|
841
|
+
?MAKE_CONFIG_OPT(public_list),
|
842
|
+
?MAKE_CONFIG_OPT(persistent),
|
843
|
+
?MAKE_CONFIG_OPT(moderated),
|
844
|
+
?MAKE_CONFIG_OPT(members_by_default),
|
845
|
+
?MAKE_CONFIG_OPT(members_only),
|
846
|
+
?MAKE_CONFIG_OPT(allow_user_invites),
|
847
|
+
?MAKE_CONFIG_OPT(password_protected),
|
848
|
+
?MAKE_CONFIG_OPT(password),
|
849
|
+
?MAKE_CONFIG_OPT(anonymous),
|
850
|
+
?MAKE_CONFIG_OPT(logging),
|
851
|
+
?MAKE_CONFIG_OPT(max_users),
|
852
|
+
{affiliations, ?DICT:to_list(StateData#state.affiliations)},
|
853
|
+
{subject, StateData#state.subject},
|
854
|
+
{subject_author, StateData#state.subject_author}
|
855
|
+
].
|
856
|
+
|
857
|
+
|
858
|
+
%%----------------------------
|
859
|
+
%% Utils
|
860
|
+
%%----------------------------
|
861
|
+
|
862
|
+
uptime_seconds() ->
|
863
|
+
trunc(element(1, erlang:statistics(wall_clock))/1000).
|
864
|
+
|
865
|
+
find_host(global) ->
|
866
|
+
global;
|
867
|
+
find_host("global") ->
|
868
|
+
global;
|
869
|
+
find_host(ServerHost) ->
|
870
|
+
gen_mod:get_module_opt_host(ServerHost, mod_muc, "conference.@HOST@").
|
871
|
+
|