Commit cb3b20ab authored by Dele Olajide's avatar Dele Olajide

ofmeet plugin version 0.1.5

Implemented Meeting Planner and automatic invites. Requires bookmarks from the clientcontrol plugin
Fixed error modified setLocalDescription failed with desktop share
Prevents sending invalid presence packets, before MUC jid is defined
Added support for web+meet: protocol handling
Added support for iNum and eNum telephone numbers
Updated Jitsi Videobridge
parent bcd1cc82
......@@ -49,6 +49,16 @@
Openfire Meetings Plugin Changelog
</h1>
<p><b>0.1.5</b> -- May 4th, 2015</p>
<ul>
<li>Implemented Meeting Planner and automatic invites. Requires bookmarks from the clientcontrol plugin</li>
<li>Fixed error modified setLocalDescription failed with desktop share</li>
<li>Prevents sending invalid presence packets, before MUC jid is defined</li>
<li>Added support for web+meet: protocol handling</li>
<li>Updated Jitsi Videobridge</li>
</ul>
<p><b>0.1.4</b> -- March 13th, 2015</p>
<ul>
......
......@@ -5,8 +5,8 @@
<name>Openfire Meetings</name>
<description>Provides high quality, scalable video conferences using Jitsi Meet and Jitsi Videobridge</description>
<author>Ignite Realtime</author>
<version>0.1.4</version>
<date>03/13/2015</date>
<version>0.1.5</version>
<date>05/04/2015</date>
<minServerVersion>3.9.9</minServerVersion>
<adminconsole>
......@@ -14,6 +14,8 @@
<sidebar id="siderbar-ofmeet" name="${plugin.title}">
<item id="ofmeet-summary" name="${config.page.summary.title}" description="${config.page.summary.description}" url="ofmeet-summary.jsp"/>
<item id="ofmeet-settings" name="${config.page.settings.title}" description="${config.page.settings.description}" url="ofmeet-settings.jsp"/>
<item id="ofmeet-planner" name="${config.page.planner.title}" description="${config.page.planner.description}" url="ofmeet-planner.jsp"/>
</sidebar>
</tab>
</adminconsole>
......
......@@ -11,7 +11,7 @@
overflow-x: hidden;
}
</style>
<script src="ofmeet.js?v=1"></script>
<script src="../chrome-extension/ofmeet-api.js?v=1"></script>
<script>
var conn = null;
var visible = false;
......
<html>
<script src="ofmeet.js?v=1"></script>
<script src="../chrome-extension/ofmeet-api.js?v=1"></script>
<script>
var proto = Object.create(HTMLElement.prototype);
......
......@@ -250,6 +250,9 @@ function generateRoomName() {
if (config.getroomnode && typeof config.getroomnode === 'function') {
// custom function might be responsible for doing the pushstate
roomnode = config.getroomnode(path);
if (roomnode.length > 13 && "web%2Bmeet%3A" == roomnode.substring(0, 13)) roomnode = roomnode.substring(14);
} else {
/* fall back to default strategy
* this is making assumptions about how the URL->room mapping happens.
......@@ -1312,6 +1315,27 @@ $(document).ready(function () {
enter_room();
});
$("#register_protocol_button").click(function()
{
try {
navigator.registerProtocolHandler("web+meet", window.location.protocol + "//" + window.location.host + "/ofmeet/?r=%s", "Openfire Meetings");
}
catch (e) {
console.error("Failed to set navigator.registerProtocolHandler " + e);
}
});
$("#unregister_protocol_button").click(function()
{
try {
navigator.unregisterProtocolHandler("web+meet", window.location.protocol + "//" + window.location.host + "/ofmeet/?r=%s");
}
catch (e) {
console.error("Failed to set navigator.unregisterProtocolHandler " + e);
}
});
$("#enter_room_field").keydown(function (event) {
if (event.keyCode === 13 /* enter */) {
enter_room();
......
var ChromeUi = (function(self) {
var rootWindow = null;
var windows = {}
var windowClosed = true;
var callbacks = {}
var videoWin = null;
var telephoneWin = null;
var optionUrl = chrome.extension.getURL('options/index.html')
var ports = {};
var speakerConnected = false;
self.createRootWindow = function()
{
chrome.windows.create({url: chrome.extension.getURL('root.html'),
focused: true,
type: "panel"
}, function (win) {
rootWindow = win
chrome.windows.update(win.id, {width: 320, height: 900});
console.log("createRootWindow", rootWindow)
windowClosed = false;
});
}
self.createNodeWindow = function(id, create, update)
{
console.log('createNodeWindow', id);
var win = windows[id]
if (win)
{
chrome.windows.update(win.window.id, update);
} else {
chrome.windows.create(create, function (win)
{
chrome.windows.update(win.id, update);
var newWin = {window: win, id: id};
windows[id] = newWin
windows[win.id] = newWin;
});
}
}
self.getRootWindow = function()
{
return rootWindow;
}
self.getNodeWindow = function(id)
{
return windows[id];
}
self.destroyNodeWindow = function(id)
{
console.log('destroyNodeWindow', id);
try {
var win = windows[id]
if (win)
{
chrome.windows.remove(win.window.id);
this.removeNodeWindow(win.window.id);
}
} catch (e) {}
}
self.removeNodeWindow = function(id)
{
console.log('removeNodeWindow', id);
try {
var win = windows[id]
console.log('removeNodeWindow', win);
if (win)
{
windows[win.window.id] = null;
windows[win.id] = null;
}
} catch (e) {}
}
self.drawAttention = function(win)
{
if (win)
{
chrome.windows.update(win.id, {drawAttention: true});
}
}
self.destroyRootWindow = function()
{
try {
console.log('destroyRootWindow');
if (!windowClosed) chrome.windows.remove(rootWindow.id);
var items = Object.getOwnPropertyNames(windows)
for (var i=0; i< items.length; i++)
{
try {
self.destroyNodeWindow(items[i]);
} catch (e) {}
}
} catch (e) {}
windowClosed = true;
}
/**
* buttons [{title: "accept", iconUrl: "accept.png"}]
* items [{ title: "Item1", message: "This is item 1."}]
* progress 0 - 100
*/
self.notifyText = function(message, context, iconUrl, buttons, callback)
{
var opt = {
type: "basic",
title: "TraderLynk",
iconUrl: iconUrl ? iconUrl : "tl_logo2.png",
message: message,
buttons: buttons,
contextMessage: context
}
var id = Math.random().toString(36).substr(2,9);
chrome.notifications.create(id, opt, function(notificationId)
{
if (callback) callbacks[notificationId] = callback;
});
};
self.notifyImage = function(message, context, imageUrl, buttons, callback)
{
var opt = {
type: "image",
title: "TraderLynk",
iconUrl: "tl_logo2.png",
message: message,
buttons: buttons,
contextMessage: context,
imageUrl: imageUrl
}
var id = Math.random().toString(36).substr(2,9);
chrome.notifications.create(id, opt, function(notificationId)
{
if (callback) callbacks[notificationId] = callback;
});
};
self.notifyProgress = function(message, context, progress, buttons, callback)
{
var opt = {
type: "progress",
title: "TraderLynk",
iconUrl: "tl_logo2.png",
message: message,
buttons: buttons,
contextMessage: context,
progress: progress
}
var id = Math.random().toString(36).substr(2,9);
chrome.notifications.create(id, opt, function(notificationId)
{
if (callback) callbacks[notificationId] = callback;
});
};
self.notifyList = function(message, context, items, buttons, callback)
{
var opt = {
type: "list",
title: "TraderLynk",
iconUrl: "tl_logo2.png",
message: message,
buttons: buttons,
contextMessage: context,
items: items
}
var id = Math.random().toString(36).substr(2,9);
chrome.notifications.create(id, opt, function(notificationId)
{
if (callback) callbacks[notificationId] = callback;
});
};
self.closeVideoWindow = function()
{
if (videoWin != null)
{
chrome.windows.remove(videoWin.id);
}
}
self.openVideoWindow = function(roomId, jid, name, callback)
{
if (videoWin == null)
{
chrome.windows.create({url: chrome.extension.getURL('conversation.video.html?id='+roomId+'&jid='+jid+'&name='+name), focused: true, type: "popup", width: 800, height: 640}, function (win)
{
videoWin = win;
if (callback) callback(videoWin);
});
} else {
chrome.windows.update(videoWin.id, {focused: true, width: 800, height: 640});
}
};
self.openTelephoneWindow = function(roomId, jid, name)
{
if (telephoneWin == null)
{
chrome.windows.create({url: chrome.extension.getURL('conversation.telephone.html?id='+roomId+'&jid='+jid+'&name='+name), focused: true, type: "panel", width: 320, height: 900}, function (win)
{
telephoneWin = win;
});
} else {
chrome.windows.update(telephoneWin.id, {focused: true, width: 320, height: 900});
}
};
self.closeTelephoneWindow = function()
{
if (telephoneWin != null)
{
chrome.windows.remove(telephoneWin.id);
}
}
self.speakerConnect = function()
{
};
self.speakerDisconnect = function()
{
};
self.speakerOn = function(roomName)
{
};
self.speakerOff = function(roomName)
{
};
self.speakerTalk = function(roomName)
{
};
self.speakerUntalk = function(roomName)
{
};
self.getOption = function(name)
{
var value = null;
try {
var key = window.localStorage[name];
if (key) value = JSON.parse(key);
} catch (e) {
console.error("getStoreSetting", e);
}
return value;
};
self.doOptions = function()
{
chrome.tabs.getAllInWindow(null, function(tabs)
{
var option_tab = tabs.filter(function(t) { return t.url === optionUrl; });
if (option_tab.length)
{
chrome.tabs.update(option_tab[0].id, {highlighted: true, active: true});
}else{
chrome.tabs.create({url: optionUrl, active: true});
}
});
}
self.publishDesktop = function(room, callback, errorback)
{
chrome.desktopCapture.chooseDesktopMedia(["screen", "window"], function(streamId)
{
});
}
window.addEventListener("load", function()
{
console.log('background-ui.js load event');
chrome.systemIndicator.enable();
chrome.browserAction.onClicked.addListener(function()
{
ChromeUi.doOptions();
});
chrome.windows.onRemoved.addListener(function(win)
{
console.log("closing window ", win);
if (rootWindow && win == rootWindow.id)
{
windowClosed = true;
rootWindow = null;
var items = Object.getOwnPropertyNames(windows)
for (var i=0; i< items.length; i++)
{
try {
self.destroyNodeWindow(items[i]);
} catch (e) {}
}
} else if (videoWin && win == videoWin.id) {
videoWin = null;
} else if (telephoneWin && win == telephoneWin.id) {
telephoneWin = null;
} else {
self.removeNodeWindow(win);
}
});
chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex)
{
var callback = callbacks[notificationId];
if (callback)
{
callback(notificationId, buttonIndex);
chrome.notifications.clear(notificationId, function(wasCleared)
{
callbacks[notificationId] = null;
delete callbacks[notificationId];
});
}
})
chrome.systemIndicator.onClicked.addListener(function()
{
console.log("background-ui.js systemIndicator.onClicked");
if (windowClosed)
{
ChromeUi.createRootWindow();
} else {
ChromeUi.destroyRootWindow();
}
});
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse)
{
});
chrome.runtime.onConnectExternal.addListener(function(port)
{
ports[port.name] = port;
port.onMessage.addListener(function(msg)
{
});
port.onDisconnect.addListener(function()
{
delete ports[port.name];
});
});
chrome.runtime.onMessage.addListener(function(request, sender)
{
});
});
window.addEventListener("beforeunload", function ()
{
console.log('background-ui.js beforeunload event');
ChromeUi.destroyRootWindow();
});
return self;
}(ChromeUi || {}));
\ No newline at end of file
/* Background page, responsible for actually choosing media */
chrome.runtime.onConnect.addListener(function (channel) {
window.addEventListener("load", function()
{
});
window.addEventListener("beforeunload", function ()
{
});
chrome.runtime.onConnect.addListener(function (channel)
{
channel.onMessage.addListener(function (message) {
switch(message.type) {
case 'ofmeetGetScreen':
......
{
"background": {
"scripts": [ "background.js" ]
"persistent": true,
"scripts": [ "background.js", "background-ui.js", "ofmeet-api.js" ]
},
"browser_action": {
"default_icon": {
"19": "icon16.png",
"38": "icon48.png"
}
},
"content_scripts": [ {
"js": [ "content.js", "jquery.min.js", "util.js", "ofmeet.js", "tinycolor.js" ],
"css": [ "cursor.css" ],
"matches": [ "https://*/*", "http://*/*" ],
"all_frames": true
} ],
"description": "Openfire Meetings Chrome Extension required for screen sharing and co-browsing",
"description": "Openfire Meetings Chrome Extension",
"icons": {
"128": "icon128.png",
"16": "icon16.png",
......@@ -17,7 +24,14 @@
"manifest_version": 2,
"minimum_chrome_version": "34",
"name": "Openfire Meetings Chrome Extension",
"permissions": [ "desktopCapture" ],
"permissions": [ "desktopCapture", "background", "management", "idle", "notifications", "tabs", "http://*/*", "https://*/*" ],
"short_name": "ofmeet chrome extension",
"version": "0.0.5"
"system_indicator": {
"default_icon": {
"19": "icon16.png",
"38": "icon48.png"
}
},
"options_page": "options/index.html",
"version": "0.0.6"
}
\ No newline at end of file
/*
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/fancy-settings
// License: LGPL v2.1
*/
.fancy {
text-shadow: #F5F5F5 0 1px 0;
}
#sidebar {
position: absolute;
background-color: #EDEDED;
background-image: linear-gradient(top, #EDEDED, #F5F5F5);
background-image: -webkit-gradient(
linear,
left top,
left 500,
color-stop(0, #EDEDED),
color-stop(1, #F5F5F5)
);
background-image: -moz-linear-gradient(
center top,
#EDEDED 0%,
#F5F5F5 100%
);
background-image: -o-linear-gradient(top, #EDEDED, #F5F5F5);
width: 219px;
top: 0;
left: 0;
bottom: 0;
border-right: 1px solid #C2C2C2;
box-shadow: inset -8px 0 30px -30px black;
}
#icon {
position: absolute;
width: 30px;
height: 30px;
top: 12px;
left: 12px;
}
#sidebar h1 {
position: absolute;
top: 13px;
right: 25px;
font-size: 26px;
color: #707070;
}
#tab-container {
position: absolute;
top: 50px;
left: 0;
right: 0;
bottom: 0;
overflow-y: auto;
overflow-x: hidden;
text-align: right;
}
#tab-container .tab {
height: 28px;
padding-right: 25px;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
font-size: 12px;
line-height: 28px;
color: #808080;
cursor: pointer;
}
#search-container {
margin-top: 5px;
margin-bottom: 5px;
padding-right: 23px !important;
cursor: default !important;
}
#search-container input {
width: 130px;
}
#tab-container .tab.active, body.searching #search-container {
background-color: #D4D4D4;
border-color: #BFBFBF;
color: black;
text-shadow: #DBDBDB 0 1px 0;
box-shadow: inset -12px 0 30px -30px black;
}
body.searching #tab-container .tab.active {
background-color: transparent;
border-color: transparent;
color: #808080;
text-shadow: inherit;
box-shadow: none;
}
#content {
position: absolute;
top: 0;
left: 220px;
right: 0;
bottom: 0;
overflow: auto;
}
.tab-content {
display: none;
position: absolute;
width: 840px;
top: 0;
left: 0;
bottom: 0;
padding: 20px;
padding-top: 15px;
}
body.searching .tab-content {
display: none !important;
}
body.searching #search-result-container {
display: block !important;
}
body.measuring .tab-content, body.measuring #search-result-container {
display: block !important;
opacity: 0;
overflow: hidden;
}
/*
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/fancy-settings
// License: LGPL v2.1
*/
.tab-content h2 {
margin: 0;
padding-bottom: 5px;
font-size: 26px;
color: #707070;
line-height: 1;
}
.setting.group {
border-top: 1px solid #EEEEEE;
margin-top: 10px;
padding-top: 5px;
padding-left: 2px;
}
.setting.group-name {
width: 140px;
padding: 0;
font-size: 14px;
font-weight: bold;
vertical-align: top;
}
.setting.bundle {
max-width: 600px;
margin-bottom: 5px;
}
.setting.bundle.list-box {
margin-bottom: 10px;
}
.setting.label.radio-buttons + .setting.container.radio-buttons {
margin-top: 3px;
}
.setting.label, .setting.element-label {
margin-right: 15px;
font-size: 13px;
font-weight: normal;
}
.setting.label.checkbox, .setting.element-label {
margin-left: 5px;
margin-right: 0;
}
.setting.label.checkbox {
position: relative;
top: 1px;
}
.setting.element.slider {
position: relative;
width: 150px;
top: 4px;
}
.setting.element.list-box {
display: block;
height: 100px;
width: 100%;
}
.setting.display.slider {
margin-left: 5px;
color: #666666;
}
#nothing-found {
display: none;
margin-top: 10px;
font-size: 18px;
font-weight: lighter;
color: #999999;
}
/*
// Add your own style rules here, not in css/main.css
// or css/setting.css for easy updating reasons.
*/
.setting.container.checkbox{
padding-bottom:10px;
}
\ No newline at end of file
// SAMPLE
this.i18n = {
"settings": {
"en": "Settings",
"de": "Optionen"
},
"search": {
"en": "Search",
"de": "Suche"
},
"nothing-found": {
"en": "No matches were found.",
"de": "Keine Übereinstimmungen gefunden."
},
"information": {
"en": "Information",
"de": "Information"
},
"login": {
"en": "Login",
"de": "Anmeldung"
},
"connection": {
"en": "Connection",
"de": "Anmeldung"
},
"username": {
"en": "Username:",
"de": "Benutzername:"
},
"server": {
"en": "Server",
"de": "Anmeldung"
},
"domain": {
"en": "Domain:",
"de": "Benutzername:"
},
"password": {
"en": "Password:",
"de": "Passwort:"
},
"x-characters": {
"en": "6 - 12 characters",
"de": "6 - 12 Zeichen"
},
"x-characters-pw": {
"en": "10 - 18 characters",
"de": "10 - 18 Zeichen"
},
"description": {
"en": "",
"de": ""
},
"logout": {
"en": "Logout",
"de": "Abmeldung"
},
"enable": {
"en": "Enable",
"de": "Aktivieren"
},
"connect": {
"en": "Connect:",
"de": "Trennen:"
},
"disconnect": {
"en": "Disconnect:",
"de": "Trennen:"
}
};
<html>
<head>
<meta charset="utf-8">
<title id="title"></title>
<!-- Stylesheets -->
<link id="favicon" rel="icon" href="../icon128.png">
<link rel="stylesheet" href="lib/default.css" media="screen">
<link rel="stylesheet" href="css/main.css" media="screen">
<link rel="stylesheet" href="css/setting.css" media="screen">
<link rel="stylesheet" href="custom.css" media="screen">
<!-- JavaScripts -->
<script src="lib/mootools-core.js"></script>
<script src="lib/store.js"></script>
<script src="js/classes/tab.js"></script>
<script src="js/classes/setting.js"></script>
<script src="js/classes/search.js"></script>
<script src="js/classes/fancy-settings.js"></script>
<script src="i18n.js"></script>
<script src="js/i18n.js"></script>
<script src="manifest.js"></script>
<script src="settings.js"></script>
<script src="webrtc.js"></script>
</head>
<body class="no-select">
<div id="sidebar" class="fancy">
<img id="icon" src="" alt=""><h1 id="settings-label"></h1>
<div id="tab-container">
<div id="search-container" class="tab">
<input id="search" type="search" placeholder="">
</div>
</div>
</div>
<div id="content">
<div id="search-result-container" class="tab-content">
<h2 id="search-label"></h2>
</div>
</div>
</body>
</html>
//
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/fancy-settings
// License: LGPL v2.1
//
(function () {
var FancySettings = this.FancySettings = new Class({
"tabs": {},
"initialize": function (name, icon) {
// Set title and icon
$("title").set("text", name);
$("favicon").set("href", icon);
$("icon").set("src", icon);
$("settings-label").set("text", (i18n.get("settings") || "Settings"));
$("search-label").set("text", (i18n.get("search") || "Search"));
$("search").set("placeholder", (i18n.get("search") || "Search") + "...");
this.tab = new Tab($("tab-container"), $("content"));
this.search = new Search($("search"), $("search-result-container"));
},
"create": function (params) {
var tab,
group,
row,
content,
bundle;
// Create tab if it doesn't exist already
if (this.tabs[params.tab] === undefined) {
this.tabs[params.tab] = {"groups":{}};
tab = this.tabs[params.tab];
tab.content = this.tab.create();
tab.content.tab.set("text", params.tab);
this.search.bind(tab.content.tab);
tab.content = tab.content.content;
(new Element("h2", {
"text": params.tab
})).inject(tab.content);
} else {
tab = this.tabs[params.tab];
}
// Create group if it doesn't exist already
if (tab.groups[params.group] === undefined) {
tab.groups[params.group] = {};
group = tab.groups[params.group];
group.content = (new Element("table", {
"class": "setting group"
})).inject(tab.content);
row = (new Element("tr")).inject(group.content);
(new Element("td", {
"class": "setting group-name",
"text": params.group
})).inject(row);
content = (new Element("td", {
"class": "setting group-content"
})).inject(row);
group.setting = new Setting(content);
} else {
group = tab.groups[params.group];
}
// Create and index the setting
bundle = group.setting.create(params);
this.search.add(bundle);
return bundle;
},
"align": function (settings) {
var types,
type,
maxWidth;
types = [
"text",
"button",
"slider",
"popupButton"
];
type = settings[0].params.type;
maxWidth = 0;
if (!types.contains(type)) {
throw "invalidType";
}
settings.each(function (setting) {
if (setting.params.type !== type) {
throw "multipleTypes";
}
var width = setting.label.offsetWidth;
if (width > maxWidth) {
maxWidth = width;
}
});
settings.each(function (setting) {
var width = setting.label.offsetWidth;
if (width < maxWidth) {
if (type === "button" || type === "slider") {
setting.element.setStyle("margin-left", (maxWidth - width + 2) + "px");
setting.search.element.setStyle("margin-left", (maxWidth - width + 2) + "px");
} else {
setting.element.setStyle("margin-left", (maxWidth - width) + "px");
setting.search.element.setStyle("margin-left", (maxWidth - width) + "px");
}
}
});
}
});
FancySettings.__proto__.initWithManifest = function (callback) {
var settings,
output;
settings = new FancySettings(manifest.name, manifest.icon);
settings.manifest = {};
manifest.settings.each(function (params) {
output = settings.create(params);
if (params.name !== undefined) {
settings.manifest[params.name] = output;
}
});
if (manifest.alignment !== undefined) {
document.body.addClass("measuring");
manifest.alignment.each(function (group) {
group = group.map(function (name) {
return settings.manifest[name];
});
settings.align(group);
});
document.body.removeClass("measuring");
}
if (callback !== undefined) {
callback(settings);
}
};
}());
//
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/fancy-settings
// License: LGPL v2.1
//
(function () {
this.Search = new Class({
"index": [],
"groups": {},
"initialize": function (search, searchResultContainer) {
var setting,
find;
this.search = search;
this.searchResultContainer = searchResultContainer;
this.setting = new Setting(new Element("div"));
// Create setting for message "nothing found"
setting = new Setting(this.searchResultContainer);
this.nothingFound = setting.create({
"type": "description",
"text": (i18n.get("nothing-found") || "No matches were found.")
});
this.nothingFound.bundle.set("id", "nothing-found");
// Create event handlers
find = (function (event) {
this.find(event.target.get("value"));
}).bind(this);
this.search.addEvent("keyup", (function (event) {
if (event.key === "esc") {
this.reset();
} else {
find(event);
}
}).bind(this));
this.search.addEventListener("search", find, false);
},
"bind": function (tab) {
tab.addEvent("click", this.reset.bind(this));
},
"add": function (setting) {
var searchSetting = this.setting.create(setting.params);
setting.search = searchSetting;
searchSetting.original = setting;
this.index.push(searchSetting);
setting.addEvent("action", function (value, stopPropagation) {
if (searchSetting.set !== undefined && stopPropagation !== true) {
searchSetting.set(value, true);
}
});
searchSetting.addEvent("action", function (value) {
if (setting.set !== undefined) {
setting.set(value, true);
}
setting.fireEvent("action", [value, true]);
});
},
"find": function (searchString) {
// Exit search mode
if (searchString.trim() === "") {
document.body.removeClass("searching");
return;
}
// Or enter search mode
this.index.each(function (setting) { setting.bundle.dispose(); });
Object.each(this.groups, function (group) { group.dispose(); });
document.body.addClass("searching");
// Filter settings
var result = this.index.filter(function (setting) {
if (setting.params.searchString.contains(searchString.trim().toLowerCase())) {
return true;
}
});
// Display settings
result.each((function (setting) {
var group,
row;
// Create group if it doesn't exist already
if (this.groups[setting.params.group] === undefined) {
this.groups[setting.params.group] = (new Element("table", {
"class": "setting group"
})).inject(this.searchResultContainer);
group = this.groups[setting.params.group];
row = (new Element("tr")).inject(group);
(new Element("td", {
"class": "setting group-name",
"text": setting.params.group
})).inject(row);
group.content = (new Element("td", {
"class": "setting group-content"
})).inject(row);
} else {
group = this.groups[setting.params.group].inject(this.searchResultContainer);
}
setting.bundle.inject(group.content);
}).bind(this));
if (result.length === 0) {
this.nothingFound.bundle.addClass("show");
} else {
this.nothingFound.bundle.removeClass("show");
}
},
"reset": function () {
this.search.set("value", "");
this.search.blur();
this.find("");
}
});
}());
//
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/fancy-settings
// License: LGPL v2.1
//
(function () {
var Bundle = new Class({
"initialize": function (creator) {
this.creator = creator;
// Create DOM elements
this.tab = new Element("div", {"class": "tab"});
this.content = new Element("div", {"class": "tab-content"});
// Create event handlers
this.tab.addEvent("click", this.activate.bind(this));
},
"activate": function () {
if (this.creator.activeBundle && this.creator.activeBundle !== this) {
this.creator.activeBundle.deactivate();
}
this.tab.addClass("active");
this.content.addClass("show");
this.creator.activeBundle = this;
},
"deactivate": function () {
this.tab.removeClass("active");
this.content.removeClass("show");
this.creator.activeBundle = null;
}
});
this.Tab = new Class({
"activeBundle": null,
"initialize": function (tabContainer, tabContentContainer) {
this.tabContainer = tabContainer;
this.tabContentContainer = tabContentContainer;
},
"create": function () {
var bundle = new Bundle(this);
bundle.tab.inject(this.tabContainer);
bundle.content.inject(this.tabContentContainer);
if (!this.activeBundle) { bundle.activate(); }
return bundle;
}
});
}());
//
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/fancy-settings
// License: LGPL v2.1
//
(function () {
var lang = navigator.language.split("-")[0];
if (this.i18n === undefined) { this.i18n = {}; }
this.i18n.get = function (value) {
if (value === "lang") {
return lang;
}
if (this.hasOwnProperty(value)) {
value = this[value];
if (value.hasOwnProperty(lang)) {
return value[lang];
} else if (value.hasOwnProperty("en")) {
return value["en"];
} else {
return Object.values(value)[0];
}
} else {
return value;
}
};
}());
/*
// Copyright (c) 2007 - 2010 blueprintcss.org
// Modified and extended by Frank Kohlhepp in 2011
// https://github.com/frankkohlhepp/default-css
// License: MIT-license
*/
/*
// Reset the default browser CSS
*/
html {
margin: 0;
padding: 0;
border: 0;
}
body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, code,
del, dfn, em, img, q, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, dialog, figure, footer, header,
hgroup, nav, section {
margin: 0;
padding: 0;
border: 0;
font-family: inherit;
font-size: 100%;
font-weight: inherit;
font-style: inherit;
vertical-align: baseline;
}
article, aside, dialog, figure, footer, header,
hgroup, nav, section {
display: block;
}
body {
background-color: white;
line-height: 1.5;
}
table {
border-collapse: separate;
border-spacing: 0;
}
caption, th, td {
text-align: left;
font-weight: normal;
}
table, th, td {
vertical-align: middle;
}
blockquote:before, blockquote:after, q:before, q:after {
content: "";
}
blockquote, q {
quotes: "" "";
}
a img {
border: none;
}
/*
// Default typography
*/
html {
font-size: 100.01%;
}
body {
background-color: white;
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 75%;
color: #222222;
}
/* Headings */
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
color: #111111;
}
h1 {
margin-bottom: 0.5em;
font-size: 3em;
line-height: 1;
}
h2 {
margin-bottom: 0.75em;
font-size: 2em;
}
h3 {
margin-bottom: 1em;
font-size: 1.5em;
line-height: 1;
}
h4 {
margin-bottom: 1.25em;
font-size: 1.2em;
line-height: 1.25;
}
h5 {
margin-bottom: 1.5em;
font-size: 1em;
font-weight: bold;
}
h6 {
font-size: 1em;
font-weight: bold;
}
h1 img, h2 img, h3 img,
h4 img, h5 img, h6 img {
margin: 0;
}
/* Text elements */
p {
margin: 0 0 1.5em;
}
.left {
float: left !important;
}
p .left {
margin: 1.5em 1.5em 1.5em 0;
padding: 0;
}
.right {
float: right !important;
}
p .right {
margin: 1.5em 0 1.5em 1.5em;
padding: 0;
}
a:focus, a:hover {
color: #0099FF;
}
a {
color: #0066CC;
text-decoration: underline;
}
blockquote {
margin: 1.5em;
font-style: italic;
color: #666666;
}
strong, dfn {
font-weight: bold;
}
em, dfn {
font-style: italic;
}
sup, sub {
line-height: 0;
}
abbr, acronym {
border-bottom: 1px dotted #666666;
}
address {
margin: 0 0 1.5em;
font-style: italic;
}
del {
color: #666666;
}
pre {
margin: 1.5em 0;
white-space: pre;
}
pre, code, tt {
font: 1em "andale mono", "lucida console", monospace;
line-height: 1.5;
}
/* Lists */
li ul, li ol {
margin: 0;
}
ul, ol {
margin: 0 1.5em 1.5em 0;
padding-left: 1.5em;
}
ul {
list-style-type: disc;
}
ol {
list-style-type: decimal;
}
dl {
margin: 0 0 1.5em 0;
}
dl dt {
font-weight: bold;
}
dd {
margin-left: 1.5em;
}
/* Tables */
table {
width: 100%;
margin-bottom: 1.4em;
}
th {
font-weight: bold;
}
table.zebra thead th, table.zebra tfoot th {
background-color: #BFBFBF;
}
th, td, caption {
padding: 4px 10px 4px 5px;
}
table.zebra tbody tr:nth-child(even) td, table.zebra tbody tr.even td {
background-color: #E6E6E6;
}
caption {
background-color: #EEEEEE;
}
/* Misc classes */
.fancy {
text-shadow: white 0 1px 0;
}
.bfancy {
text-shadow: black 0 1px 0;
}
.fancyt {
text-shadow: white 0 -1px 0;
}
.bfancyt {
text-shadow: black 0 -1px 0;
}
.no-fancy {
text-shadow: none;
}
.select {
cursor: auto;
user-select: auto;
-webkit-user-select: auto;
-moz-user-select: auto;
-o-user-select: auto;
}
img.select, .select img {
user-drag: auto;
-webkit-user-drag: auto;
-moz-user-drag: auto;
-o-user-drag: auto;
}
.no-select {
cursor: default;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-o-user-select: none;
}
img.no-select, .no-select img {
user-drag: none;
-webkit-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
}
.focus:focus, .focus :focus {
outline: auto;
}
.no-focus:focus, .no-focus :focus {
outline: 0;
}
.small {
margin-bottom: 1.875em;
font-size: .8em;
line-height: 1.875em;
}
.large {
margin-bottom: 1.25em;
font-size: 1.2em;
line-height: 2.5em;
}
.show {
display: block !important;
}
.show-inline {
display: inline-block !important;
}
.hide {
display: none;
}
.quiet {
color: #666666;
}
.loud {
color: black;
}
.highlight {
background-color: yellow;
}
.added {
background-color: #006600;
color: white;
}
.removed {
background-color: #990000;
color: white;
}
.first {
margin-left: 0;
padding-left: 0;
}
.last {
margin-right: 0;
padding-right: 0;
}
.top {
margin-top: 0;
padding-top: 0;
}
.bottom {
margin-bottom: 0;
padding-bottom: 0;
}
/*
// Default styling for forms
*/
fieldset {
margin: 0 0 1.5em 0;
padding: 0 1.4em 1.4em 1.4em;
border: 1px solid #CCCCCC;
}
legend {
margin-top: -0.2em;
margin-bottom: 1em;
font-weight: bold;
font-size: 1.2em;
}
/* Form fields */
input[type=text], input[type=password], textarea {
background-color: white;
border: 1px solid #BBBBBB;
}
input[type=text], input[type=password],
textarea, select {
margin: 0.5em 0;
}
input[type=text], input[type=password] {
width: 300px;
padding: 4px;
}
textarea {
width: 450px;
height: 170px;
padding: 5px;
}
/* success, info, notice and error boxes */
.success, .info, .notice, .error {
margin-bottom: 1em;
padding: 0.8em;
border: 2px solid #DDDDDD;
}
.success {
background-color: #E6EFC2;
border-color: #C6D880;
color: #264409;
}
.info {
background-color: #D5EDF8;
border-color: #92CAE4;
color: #205791;
}
.notice {
background-color: #FFF6BF;
border-color: #FFD324;
color: #514721;
}
.error {
background-color: #FBE3E4;
border-color: #FBC2C4;
color: #8A1F11;
}
.success a {
color: #264409;
}
.info a {
color: #205791;
}
.notice a {
color: #514721;
}
.error a {
color: #8A1F11;
}
//
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/store-js
// License: MIT-license
//
(function () {
var Store = this.Store = function (name, defaults) {
var key;
this.name = name;
if (defaults !== undefined) {
for (key in defaults) {
if (defaults.hasOwnProperty(key) && this.get(key) === undefined) {
this.set(key, defaults[key]);
}
}
}
};
Store.prototype.get = function (name) {
name = "store." + this.name + "." + name;
if (localStorage.getItem(name) === null) { return undefined; }
try {
return JSON.parse(localStorage.getItem(name));
} catch (e) {
return null;
}
};
Store.prototype.set = function (name, value) {
if (value === undefined) {
this.remove(name);
} else {
if (typeof value === "function") {
value = null;
} else {
try {
value = JSON.stringify(value);
} catch (e) {
value = null;
}
}
localStorage.setItem("store." + this.name + "." + name, value);
}
return this;
};
Store.prototype.remove = function (name) {
localStorage.removeItem("store." + this.name + "." + name);
return this;
};
Store.prototype.removeAll = function () {
var name,
i;
name = "store." + this.name + ".";
for (i = (localStorage.length - 1); i >= 0; i--) {
if (localStorage.key(i).substring(0, name.length) === name) {
localStorage.removeItem(localStorage.key(i));
}
}
return this;
};
Store.prototype.toObject = function () {
var values,
name,
i,
key,
value;
values = {};
name = "store." + this.name + ".";
for (i = (localStorage.length - 1); i >= 0; i--) {
if (localStorage.key(i).substring(0, name.length) === name) {
key = localStorage.key(i).substring(name.length);
value = this.get(key);
if (value !== undefined) { values[key] = value; }
}
}
return values;
};
Store.prototype.fromObject = function (values, merge) {
if (merge !== true) { this.removeAll(); }
for (var key in values) {
if (values.hasOwnProperty(key)) {
this.set(key, values[key]);
}
}
return this;
};
}());
this.manifest = {
"name": "Openfire Meetings Options",
"icon": "../icon128.png",
"settings": [
{
"tab": i18n.get("information"),
"group": i18n.get("connection"),
"name": "server",
"type": "text",
"label": i18n.get("server"),
"text": i18n.get("x-characters")
},
{
"tab": i18n.get("information"),
"group": i18n.get("connection"),
"name": "domain",
"type": "text",
"label": i18n.get("domain"),
"text": i18n.get("x-characters-pw")
},
{
"tab": i18n.get("information"),
"group": i18n.get("login"),
"name": "username",
"type": "text",
"label": i18n.get("username"),
"text": i18n.get("x-characters")
},
{
"tab": i18n.get("information"),
"group": i18n.get("login"),
"name": "password",
"type": "text",
"label": i18n.get("password"),
"text": i18n.get("x-characters-pw"),
"masked": true
},
{
"tab": i18n.get("information"),
"group": i18n.get("login"),
"name": "myDescription",
"type": "description",
"text": i18n.get("description")
},
{
"tab": i18n.get("information"),
"group": i18n.get("login"),
"name": "connect",
"type": "button",
"label": i18n.get("connect"),
"text": i18n.get("login")
},
{
"tab": i18n.get("information"),
"group": i18n.get("logout"),
"name": "myCheckbox",
"type": "checkbox",
"label": i18n.get("enable")
},
{
"tab": i18n.get("information"),
"group": i18n.get("logout"),
"name": "disconnect",
"type": "button",
"label": i18n.get("disconnect"),
"text": i18n.get("logout")
},
{
"tab": i18n.get("Audio Hardware"),
"group": i18n.get("Sip Speaker"),
"name": "sipSpeakerAddress",
"type": "text",
"label": i18n.get("Sip address"),
"text": i18n.get("x-characters")
},
{
"tab": i18n.get("Audio Hardware"),
"group": i18n.get("Sip Speaker"),
"name": "sipEnableCheckbox",
"type": "checkbox",
"label": i18n.get("Enable Sip Device")
},
{
"tab": i18n.get("Audio Hardware"),
"group": i18n.get("Sip Speaker"),
"name": "savesipSpeakerAddress",
"type": "button",
"label": i18n.get("Save settings"),
"text": i18n.get("Save")
}
],
"alignment": [
[
"username",
"password"
]
]
};
window.addEvent("domready", function () {
new FancySettings.initWithManifest(function (settings)
{
var background = chrome.extension.getBackgroundPage();
settings.manifest.disconnect.addEvent("action", function ()
{
console.log("Logged out!");
});
settings.manifest.connect.addEvent("action", function ()
{
reloadTL()
});
settings.manifest.savesipSpeakerAddress.addEvent("action", function ()
{
reloadTL()
});
function reloadTL(){
background.ChromeApi.reloadTL();
}
});
});
window.addEventListener("load", function()
{
navigator.webkitGetUserMedia({ audio: true, video: true },
function (stream) {
setTimeout(function() {stream.stop();}, 1000);
},
function (error) {
alert("To experience the full functionality of Openfire Meetings, please connect audio and video devices.");
console.error("Error trying to get the stream:: " + error.message);
}
);
});
\ No newline at end of file
......@@ -4,6 +4,10 @@ config.page.summary.title=Meetings Summary
config.page.summary.description=Openfire Meetings Summary Page
config.page.settings.title=Meetings Settings
config.page.settings.description=Openfire Meetings Settings Page
config.page.calendar.title=Meeting Room Calendar
config.page.calendar.description=Openfire Meetings Room Calendar Page
config.page.planner.title=Meetings Planner
config.page.planner.description=Openfire Meetings Planner Page
config.page.configuration.media.title=Media Configuration
config.page.configuration.security.title=Security
config.page.configuration.recording.title=Recordings
......@@ -85,3 +89,11 @@ ofmeet.summary.is.expired=Expired?
ofmeet.summary.is.recording=Recording?
ofmeet.summary.no.conferences=No active conferences
ofmeet.summary.expire=Expire
ofmeet.planner.title=Meetings Planner
ofmeet.planner.description=Summary of all planned meetings. Select a meeting room to manage the calendar
ofmeet.planner.name=Meeting Room Name
ofmeet.planner.address=Meeting Room Address
ofmeet.planner.users=user(s)
ofmeet.planner.groups=groups(s)
ofmeet.planner.none=No planned meetings available
ofmeet.calendar.description=Click on calendar to select or create a planned meeting
......@@ -117,7 +117,7 @@
<div id="brand_header"></div>
<input type='checkbox' name='checkbox' id="disable_welcome"/>
<label for="disable_welcome" class="disable_welcome_position">Don't show this page next time I enter</label>
<div id="header_text"></div>
<div id="header_text"><input id="register_protocol_button" type="button" value="Register web+meet" /><input id="unregister_protocol_button" type="button" value="Unregister web+meet" /></div>
</div>
<div id="welcome_page_main">
<div id="features">
......
This diff is collapsed.
......@@ -21,8 +21,10 @@ import javax.media.format.*;
import org.jivesoftware.util.*;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.muc.*;
import org.jivesoftware.openfire.session.*;
import org.slf4j.*;
import org.slf4j.Logger;
......@@ -56,7 +58,7 @@ import org.ifsoft.*;
import org.ifsoft.sip.*;
import net.sf.fmj.media.rtp.*;
import org.ifsoft.rtp.*;
import uk.nominet.DDDS.*;
public class CallControlComponent extends AbstractComponent
{
......@@ -81,7 +83,7 @@ public class CallControlComponent extends AbstractComponent
self = this;
Properties properties = new Properties();
String hostName = JiveGlobals.getProperty("org.jitsi.videobridge.nat.harvester.public.address", XMPPServer.getInstance().getServerInfo().getHostname());
String hostName = JiveGlobals.getProperty("org.jitsi.videobridge.nat.harvester.public.address", XMPPServer.getInstance().getServerInfo().getXMPPDomain());
String logDir = pluginDirectory.getAbsolutePath() + File.separator + ".." + File.separator + ".." + File.separator + "logs" + File.separator;
String port = JiveGlobals.getProperty("org.jitsi.videobridge.sip.port.number", "5060");
......@@ -162,7 +164,7 @@ public class CallControlComponent extends AbstractComponent
Log.info("CallControlComponent - makeCall " + confJid + " " + to + " " + callId);
try {
String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
String hostname = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
String callerId = (new JID(confJid)).getNode();
String focusJid = conference.getFocus();
......@@ -192,14 +194,47 @@ public class CallControlComponent extends AbstractComponent
if (!toSip && !toPhone)
{
to = "tel:" + to;
toPhone = true;
String sipUri = null;
if (to.length() == 8)
{
toSip = true;
to = "sip:8835100" + to + "@81.201.82.25";
} else {
if (to.indexOf("+") != 0) to = "+" + to;
Log.info("CallControlComponent - makeCall looking up " + to);
ENUM mEnum = new ENUM("e164.arpa");
Rule[] rules = mEnum.lookup(to);
for (Rule rule: rules)
{
String temp = rule.evaluate();
Log.info("CallControlComponent - makeCall found " + temp);
if (temp.indexOf("sip:") == 0) sipUri = temp;
}
if (sipUri != null)
{
toSip = true;
to = sipUri;
} else {
to = "tel:" + to;
toPhone = true;
}
}
}
if (toSip)
{
from = "sip:" + callerId + "@" + hostname;
Log.info("CallControlComponent - makeCall with direct sip " + to + " " + from);
} else {
to = to.substring(4);
......@@ -261,14 +296,14 @@ public class CallControlComponent extends AbstractComponent
Log.info("CallControlComponent - findCreateSession " + from + " " + to + " " + destination);
CallSession session = null;
String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
String hostname = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
String callerId = to;
String confJID = null;
Conference conference = null;
boolean allowDirectSIP = "true".equals(JiveGlobals.getProperty("org.jitsi.videobridge.ofmeet.allow.direct.sip", "false"));
if (!allowDirectSIP && !registrations.containsKey(from))
if (!allowDirectSIP || !registrations.containsKey(from))
{
Log.warn("CallControlComponent - call rejected from " + from + " " + to);
return null; // only accept calls from registered SIP user endpoint
......@@ -361,39 +396,73 @@ public class CallControlComponent extends AbstractComponent
MultiUserChatService service = mucManager.getMultiUserChatService(confJID);
if (service.hasChatRoom(conferenceId))
if (service != null)
{
MUCRoom room = service.getChatRoom(conferenceId);
if (room != null)
if (service.hasChatRoom(conferenceId))
{
for (MUCRole role : room.getOccupants())
{
Presence presence = new Presence();
presence.setFrom(callId + "@" + getJID());
presence.setTo(role.getUserAddress());
MUCRoom room = service.getChatRoom(conferenceId);
if (accepted)
if (room != null)
{
for (MUCRole role : room.getOccupants())
{
Element answered = presence.addChildElement("answered", "urn:xmpp:rayo:1");
answered.addElement("header").addAttribute("name", "caller_id").addAttribute("value", session.jabberRemote);
answered.addElement("header").addAttribute("name", "called_id").addAttribute("value", session.jabberLocal);
Presence presence = new Presence();
presence.setFrom(callId + "@" + getJID());
presence.setTo(role.getUserAddress());
if (accepted)
{
Element answered = presence.addChildElement("answered", "urn:xmpp:rayo:1");
answered.addElement("header").addAttribute("name", "caller_id").addAttribute("value", session.jabberRemote);
answered.addElement("header").addAttribute("name", "called_id").addAttribute("value", session.jabberLocal);
} else {
Element hangup = presence.addChildElement("hangup", "urn:xmpp:rayo:1");
hangup.addElement("header").addAttribute("name", "caller_id").addAttribute("value", session.jabberRemote);
hangup.addElement("header").addAttribute("name", "called_id").addAttribute("value", session.jabberLocal);
} else {
Element hangup = presence.addChildElement("hangup", "urn:xmpp:rayo:1");
hangup.addElement("header").addAttribute("name", "caller_id").addAttribute("value", session.jabberRemote);
hangup.addElement("header").addAttribute("name", "called_id").addAttribute("value", session.jabberLocal);
callSessions.remove(callId);
}
callSessions.remove(callId);
sendPacket(presence);
}
sendPacket(presence);
return;
}
}
}
// no valid muc service or room. send events to requestor
String username = (new JID(session.focusJID)).getNode();
Collection<ClientSession> sessions = SessionManager.getInstance().getSessions(username);
for (ClientSession clientSession : sessions)
{
Presence presence = new Presence();
presence.setFrom(callId + "@" + getJID());
presence.setTo(clientSession.getAddress());
if (accepted)
{
Element answered = presence.addChildElement("answered", "urn:xmpp:rayo:1");
answered.addElement("header").addAttribute("name", "caller_id").addAttribute("value", session.jabberRemote);
answered.addElement("header").addAttribute("name", "called_id").addAttribute("value", session.jabberLocal);
} else {
Element hangup = presence.addChildElement("hangup", "urn:xmpp:rayo:1");
hangup.addElement("header").addAttribute("name", "caller_id").addAttribute("value", session.jabberRemote);
hangup.addElement("header").addAttribute("name", "called_id").addAttribute("value", session.jabberLocal);
callSessions.remove(callId);
}
sendPacket(presence);
}
} catch (Exception e) {
Log.error("CallControlComponent inviteEvent. error finding room " + session.roomJID, e);
Log.error("CallControlComponent inviteEvent. error" + session.roomJID, e);
}
} else {
......@@ -430,20 +499,32 @@ public class CallControlComponent extends AbstractComponent
String name = header.attributeValue("name");
String value = header.attributeValue("value");
if ("JvbRoomId".equals(name)) confId = value;
if ("JvbRoomName".equals(name)) confJid = value;
}
if (confJid == null)
if (confJid == null && confId == null)
{
reply.setError(PacketError.Condition.item_not_found);
Log.error("No JvbRoomName header found");
Log.error("No JvbRoomName or JvbRoomId header found");
} else {
if (conferences.containsKey(confJid))
if (confId == null)
{
confId = conferences.get(confJid);
if (conferences.containsKey(confJid))
{
confId = conferences.get(confJid);
}
}
if (confJid == null)
{
confJid = confId + "@conference." + XMPPServer.getInstance().getServerInfo().getXMPPDomain();
}
if (confId != null)
{
String callId = Long.toHexString(System.currentTimeMillis());
Log.info("Got dial request " + from + " -> " + to + " confId " + confId + " callId " + callId);
......
......@@ -16,6 +16,8 @@ import org.jitsi.videobridge.osgi.*;
import org.osgi.framework.*;
import org.xmpp.component.*;
import org.xmpp.packet.*;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Packet;
/**
* Implements <tt>org.xmpp.component.Component</tt> to provide Jitsi Videobridge
......@@ -208,6 +210,31 @@ public class ComponentImpl
return iq;
}
/**
* Handles a <tt>GracefulShutdownIQ</tt> stanza which represents a request.
*
* @param shutdownIQ the <tt>GracefulShutdownIQ</tt> stanza represents
* the request to handle
* @return an <tt>org.jivesoftware.smack.packet.IQ</tt> stanza which
* represents the response to the specified request or <tt>null</tt> to
* reply with <tt>feature-not-implemented</tt>
* @throws Exception to reply with <tt>internal-server-error</tt> to the
* specified request
*/
private org.jivesoftware.smack.packet.IQ handleGracefulShutdownIQ(
GracefulShutdownIQ shutdownIQ)
throws Exception
{
Videobridge videobridge = getVideobridge();
org.jivesoftware.smack.packet.IQ iq;
if (videobridge == null)
iq = null;
else
iq = videobridge.handleGracefulShutdownIQ(shutdownIQ);
return iq;
}
/**
* Handles an <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>get</tt> or
* <tt>set</tt> which represents a request. Converts the specified
......@@ -351,6 +378,8 @@ public class ComponentImpl
if (request instanceof ColibriConferenceIQ)
response = handleColibriConferenceIQ((ColibriConferenceIQ) request);
else if (request instanceof GracefulShutdownIQ)
response = handleGracefulShutdownIQ((GracefulShutdownIQ)request);
else
response = null;
return response;
......@@ -544,4 +573,4 @@ public class ComponentImpl
this.bundleContext = null;
}
}
}
}
\ No newline at end of file
......@@ -23,10 +23,12 @@ import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.*;
import java.text.*;
import java.util.regex.*;
import org.xmpp.packet.*;
import org.jivesoftware.util.*;
import org.jivesoftware.openfire.plugin.spark.*;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.http.HttpBindManager;
......@@ -40,6 +42,7 @@ import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.muc.*;
import org.jivesoftware.openfire.group.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -58,6 +61,9 @@ import org.jitsi.videobridge.openfire.PluginImpl;
import org.jitsi.jigasi.openfire.JigasiPlugin;
import org.jitsi.jicofo.openfire.JicofoPlugin;
import net.sf.json.*;
public class OfMeetPlugin implements Plugin, ClusterEventListener {
private static final Logger Log = LoggerFactory.getLogger(OfMeetPlugin.class);
......@@ -67,6 +73,10 @@ public class OfMeetPlugin implements Plugin, ClusterEventListener {
private JicofoPlugin jicofoPlugin;
private PluginManager manager;
public File pluginDirectory;
private TaskEngine taskEngine = TaskEngine.getInstance();
private UserManager userManager = XMPPServer.getInstance().getUserManager();
public static OfMeetPlugin self;
public String sipRegisterStatus = "";
......@@ -89,6 +99,7 @@ public class OfMeetPlugin implements Plugin, ClusterEventListener {
this.manager = manager;
this.pluginDirectory = pluginDirectory;
self = this;
try {
Log.info("OfMeet Plugin - Initialize jitsi videobridge ");
......@@ -103,7 +114,6 @@ public class OfMeetPlugin implements Plugin, ClusterEventListener {
Log.info("OfMeet Plugin - Initialize jitsi conference focus");
UserManager userManager = XMPPServer.getInstance().getUserManager();
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
String userName = "focus";
String focusUserJid = userName + "@" + domain;
......@@ -143,6 +153,26 @@ public class OfMeetPlugin implements Plugin, ClusterEventListener {
}
}, 5000);
try {
boolean clientControl = XMPPServer.getInstance().getPluginManager().getPlugin("clientControl") != null || XMPPServer.getInstance().getPluginManager().getPlugin("clientcontrol") != null;
if (clientControl)
{
new Timer().scheduleAtFixedRate(new TimerTask()
{
@Override public void run()
{
processMeetingPlanner();
}
}, 0, 900000);
}
} catch (Exception e) {
Log.error("Meeting Planner Executor error", e);
}
ClusterManager.addListener(this);
......@@ -257,8 +287,150 @@ public class OfMeetPlugin implements Plugin, ClusterEventListener {
public void markedAsSeniorClusterMember()
{
Log.info("OfMeet Plugin - markedAsSeniorClusterMember");
jitsiPlugin.initializePlugin(manager, pluginDirectory);
jigasiPlugin.initializePlugin(manager, pluginDirectory);
jicofoPlugin.initializePlugin(manager, pluginDirectory);
}
public void processMeetingPlanner()
{
Log.info("OfMeet Plugin - processMeetingPlanner");
final Collection<Bookmark> bookmarks = BookmarkManager.getBookmarks();
for (Bookmark bookmark : bookmarks)
{
String json = bookmark.getProperty("calendar");
if (json != null)
{
bookmark.setProperty("lock", "true");
JSONArray calendar = new JSONArray(json);
boolean done = false;
for(int i = 0; i < calendar.length(); i++)
{
JSONObject meeting = calendar.getJSONObject(i);
boolean processed = meeting.getBoolean("processed");
long startLong = meeting.getLong("startTime");
Date rightNow = new Date(System.currentTimeMillis());
Date actionDate = new Date(startLong + 300000);
Date warnDate = new Date(startLong - 960000);
Log.debug("OfMeet Plugin - scanning meeting now " + rightNow + " action " + actionDate + " warn " + warnDate + "\n" + meeting );
if(rightNow.after(warnDate) && rightNow.before(actionDate))
{
for (String user : bookmark.getUsers())
{
processMeeting(meeting, user);
}
for (String groupName : bookmark.getGroups())
{
try {
Group group = GroupManager.getInstance().getGroup(groupName);
for (JID memberJID : group.getMembers())
{
processMeeting(meeting, memberJID.getNode());
}
} catch (GroupNotFoundException e) { }
}
meeting.put("processed", true);
done = true;
}
}
if (done)
{
json = calendar.toString();
bookmark.setProperty("calendar", json);
Log.debug("OfMeet Plugin - processed meeting\n" + json);
}
bookmark.setProperty("lock", "false");
}
}
}
private void processMeeting(JSONObject meeting, String username)
{
Log.info("OfMeet Plugin - processMeeting " + username + " " + meeting);
try {
User user = userManager.getUser(username);
Date start = new Date(meeting.getLong("startTime"));
Date end = new Date(meeting.getLong("startTime"));
String name = user.getName();
String email = user.getEmail();
String description = meeting.getString("description");
String title = meeting.getString("title");
String room = meeting.getString("room");
String url = "https://" + XMPPServer.getInstance().getServerInfo().getHostname() + ":" + JiveGlobals.getProperty("httpbind.port.secure", "7443") + "/ofmeet/?r=" + room;
String template = JiveGlobals.getProperty("ofmeet.email.template", "Dear [name],\n\nYou have an online meeting from [start] to [end]\n\n[description]\n\nTo join, please click\n[url]\n\nAdministrator - [domain]");
HashMap variables = new HashMap<String, String>();
if (email != null)
{
variables.put("name", name);
variables.put("email", email);
variables.put("start", start.toString());
variables.put("end", end.toString());
variables.put("description", description);
variables.put("title", title);
variables.put("room", room);
variables.put("url", url);
variables.put("domain", XMPPServer.getInstance().getServerInfo().getXMPPDomain());
sendEmail(name, email, title, replaceTokens(template, variables), null);
}
}
catch (Exception e) {
Log.error("processMeeting error", e);
}
}
private void sendEmail(String toName, String toAddress, String subject, String body, String htmlBody)
{
try {
String fromAddress = "no_reply@" + JiveGlobals.getProperty("ofmeet.email.domain", XMPPServer.getInstance().getServerInfo().getXMPPDomain());
String fromName = JiveGlobals.getProperty("ofmeet.email.fromname", "Openfire Meetings");
Log.debug( "sendEmail " + toAddress + " " + subject + "\n " + body + "\n " + htmlBody);
EmailService.getInstance().sendMessage(toName, toAddress, fromName, fromAddress, subject, body, htmlBody);
}
catch (Exception e) {
Log.error(e.toString());
}
}
private String replaceTokens(String text, Map<String, String> replacements)
{
Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(text);
StringBuffer buffer = new StringBuffer();
while (matcher.find())
{
String replacement = replacements.get(matcher.group(1));
if (replacement != null)
{
matcher.appendReplacement(buffer, "");
buffer.append(replacement);
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
}
......@@ -52,7 +52,7 @@ public class BookmarkManager {
* @return the bookmark.
* @throws NotFoundException if the bookmark could not be found or loaded.
*/
public Bookmark getBookmark(long bookmarkID) throws NotFoundException {
public static Bookmark getBookmark(long bookmarkID) throws NotFoundException {
// TODO add caching
return new Bookmark(bookmarkID);
}
......
/*
* Copyright 2009 Nominet UK
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.nominet.DDDS;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This is an abstract implementation of the Dynamic Delegation Discovery
* System (DDDS) as described in <a href="http://tools.ietf.org/html/rfc3401">RFC 3401</a>
* and <a href="http://tools.ietf.org/html/rfc3402">RFC 3402</a>.
*
* @author Ray Bellis
*
*/
public abstract class DDDS {
protected abstract String convertKeyToAUS(String key);
protected abstract String convertAUSToDBKey(String key);
protected abstract List<Rule> lookupRules(String aus, String key);
/**
* Implements the DDDS "first well known rule"
*
* The default first well known rule is an "identity" operation
*
* @param key the input value to the rule
* @return the result of the first well known rule
*/
protected String applyFirstWellKnownRule(String key) {
return key;
}
final private void internalLookup(String aus, String key, List<Rule> rules) {
/* lookup all rules matching the supplied AUS */
List<Rule> aRules = lookupRules(aus, key);
if (aRules == null) {
return;
}
/* and add them to the list */
for (Rule rule: aRules) {
if (rule.isTerminal()) {
rules.add(rule);
} else {
/* recurse if necessary */
internalLookup(aus, rule.evaluate(), rules);
}
}
}
/**
* <p>Implements a DDDS lookup.</p>
*
* @param input the input key, in generic format
* @return a list of {@link Rule} objects
*/
final public Rule[] lookup(String input) {
String aus = convertKeyToAUS(input);
String key = applyFirstWellKnownRule(aus);
key = convertAUSToDBKey(key);
ArrayList<Rule> rules = new ArrayList<Rule>();
internalLookup(aus, key, rules);
Rule[] res = rules.toArray(new Rule[0]);
Arrays.sort(res);
return res;
}
}
/*
* Copyright 2009 Nominet UK
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.nominet.DDDS;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xbill.DNS.ExtendedResolver;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.NAPTRRecord;
import org.xbill.DNS.Name;
import org.xbill.DNS.Record;
import org.xbill.DNS.Resolver;
import org.xbill.DNS.Type;
public abstract class DDDSinDNS extends DDDS {
public List<Rule> lookupRules(String aus, String key) {
ArrayList<Rule> rules = new ArrayList<Rule>();
try {
Resolver resolver = new ExtendedResolver();
resolver.setTimeout(2);
Lookup lookup = new Lookup(key, Type.NAPTR);
lookup.setResolver(resolver);
Record[] records = lookup.run();
if (records != null) {
for (int i = 0; i < records.length; ++i) {
// type check necessary in case of other RRtypes in the
// Answer
// Section
if (records[i] instanceof NAPTRRecord) {
parseAndAddRule(aus, (NAPTRRecord) records[i], rules);
}
}
}
} catch (Exception e) {
/* do nothing */
}
return rules;
}
/**
* This method parses a {@link NAPTRRecord} and adds it to a list of rules
*/
protected abstract void parseAndAddRule(String aus, NAPTRRecord naptr, List<Rule> rules);
/**
* dnsjava returns strings in presentation format, so real backslashes in
* the input are doubled. This removes them.
*/
protected final static String unescape(String input) {
char[] c = input.toCharArray();
StringBuffer sb = new StringBuffer(input.length());
for (int i = 0; i < c.length; ++i) {
if (c[i] == '\\') {
// @todo - may fall off the end
i++;
}
sb.append(c[i]);
}
return sb.toString();
}
/**
* DNSRule encapsulates the contents of a NAPTR record, as an abstract
* implementation of the DDDS {@link Rule} interface.
*
* Full implementation requires creation of a concrete subclass.
*
* @see Rule
*/
public abstract class DNSRule implements Rule, Comparable<DNSRule> {
protected String aus;
protected int order;
protected int preference;
protected String flags;
protected String service;
protected String regexp;
protected Name replacement;
/**
* Compares the current DNSRule object to another one. The comparison
* follows the standard DDDS rule, namely that rules are sorted first by
* "order", and then by "priority".
*
* @param r
* The rule to compare against
* @return negative, zero, or positive value
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(DNSRule r) {
if (order != r.order) {
return (order - r.order);
} else {
return (preference - r.preference);
}
}
public int getOrder() {
return order;
}
public int getPriority() {
return preference;
}
public String getFlags() {
return flags;
}
public String getService() {
return service;
}
public DNSRule(String aus, int order, int preference, String flags,
String service, String regexp, Name replacement) {
this.aus = aus;
this.preference = preference;
this.flags = flags;
this.service = service;
this.regexp = regexp;
this.replacement = replacement;
}
public DNSRule(String aus, NAPTRRecord naptr) {
this.aus = aus;
order = naptr.getOrder();
preference = naptr.getPreference();
flags = unescape(naptr.getFlags());
service = unescape(naptr.getService());
regexp = unescape(naptr.getRegexp());
replacement = naptr.getReplacement();
}
public String evaluate() {
String search = null;
String replace = null;
if (regexp == null || regexp.length() <= 0 || !isTerminal()) {
return replacement.toString();
}
char[] c = regexp.toCharArray();
char delim = c[0];
// @todo - more sanity checking
int i, j;
for (i = 1; i < c.length; i++) {
if (c[i] == '\\')
continue;
if (c[i] == delim) {
search = regexp.substring(1, i++);
break;
}
}
for (j = i; j < c.length; ++j) {
if (c[j] == '\\')
continue;
if (c[j] == delim) {
replace = regexp.substring(i, j);
break;
}
}
// failed to parse - crap out here
if (search == null || replace == null) {
return null;
}
// convert \digit to $digit - Java regexps aren't the same
// as the same as NAPTR records
replace = replace.replaceAll("\\\\(\\d)", "\\$$1");
// @todo - support case insensitive flag
Pattern p = Pattern.compile(search);
Matcher m = p.matcher(aus);
if (m.matches()) {
return m.replaceFirst(replace);
} else {
return null;
}
}
public String toString() {
return service + " " + evaluate();
}
public abstract boolean isTerminal();
}
}
/*
* Copyright 2009 Nominet UK
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.nominet.DDDS;
import java.util.List;
import org.xbill.DNS.NAPTRRecord;
import org.xbill.DNS.Name;
/**
* An implementation of an ENUM (<a href="http://tools.ietf.org/html/rfc3761">RFC 3761</a>)
* database lookup system, written as a concrete implementation of {@link DDDSinDNS}
*
* @author Ray Bellis
* @see DDDSinDNS
*/
public class ENUM extends DDDSinDNS {
private String suffix = null;
/**
*
* @param suffix The DNS domain name suffix to be appended to all ENUM lookups in the DNS
*/
public ENUM(String suffix) {
this.suffix = suffix;
}
/**
* Constructs an ENUM object with a default suffix of "e164.arpa".
*/
public ENUM() {
this("e164.arpa");
}
/**
* Converts the supplied string into ENUM's application unique
* string format, that is an E.164 number prefixed with a '+'
* and with any other non-digit characters removed.
*
* See 2.1 of <a href="http://tools.ietf.org/html/rfc3761">RFC 3761</a>
*
* @param input the number being looked up
* @return the number in AUS format
* @see uk.nominet.DDDS.DDDS#convertToAUS(java.lang.String)
*/
protected String convertKeyToAUS(String input) {
char[] ca = input.toCharArray();
int len = ca.length;
StringBuffer sb = new StringBuffer(len);
sb.append('+');
for (int i = 0; i < len; ++i) {
char c = ca[i];
if (Character.isDigit(c)) {
sb.append(c);
}
}
return sb.toString();
}
/**
* <p>Converts an input value (in Application Unique String format) into the
* database key (in this case a domain name).</p>
*
* <p>The ENUM key format is the AUS (with the leading '+' removed) in reverse
* order, with each number treated as a separate label. The current suffix
* is then appended, eg.:</p>
*
* <code>+441865332211</code> becomes
* <code>1.1.2.2.3.3.5.6.8.1.4.4.e164.arpa.</code>
*
* @param key The Application Unique String
* @return The AUS converted to ENUM database format (e.g. a domain name)
* @see uk.nominet.DDDS.DDDS#convertToDatabaseKey(java.lang.String)
*/
protected String convertAUSToDBKey(String key) {
char[] ca = key.toCharArray();
int len = ca.length;
StringBuffer sb = new StringBuffer(len * 2 + suffix.length());
for (int i = len - 1; i > 0; --i) {
sb.append(ca[i]);
sb.append('.');
}
sb.append(suffix);
return sb.toString();
}
/**
* @see uk.nominet.DDDS.DDDSinDNS#parseAndAddRule(java.util.List, java.lang.String, org.xbill.DNS.NAPTRRecord)
*/
@Override
protected void parseAndAddRule(String aus, NAPTRRecord naptr,
List<Rule> rules)
{
// split service field on '+' token
String service = unescape(naptr.getService());
String[] services = service.toLowerCase().split("\\+");
// check that resulting fields are valid
if (services.length < 2) return; // not x+y
if (!services[0].equals("e2u")) return; // not E2U+...
for (int i = 1; i < services.length; ++i) {
EnumRule rule = new EnumRule(aus,
naptr.getOrder(),
naptr.getPreference(),
unescape(naptr.getFlags()),
"E2U+" + services[i],
unescape(naptr.getRegexp()),
naptr.getReplacement(),
i);
rules.add(rule);
}
}
public class EnumRule extends DNSRule {
protected int rank;
public EnumRule(String aus, int order, int preference, String flags,
String service, String regexp, Name replacement, int rank)
{
super(aus, order, preference, flags, service, regexp, replacement);
this.rank = rank;
}
public boolean isTerminal() {
return flags.toLowerCase().equals("u");
}
/**
* Extends the {@link DNSRule#compareTo()} method by also looking at the {@link rank}
* field. This field is used to remember the order of entries when the ENUM Service
* field is of the form E2U+s1+s2+...
*
* @see uk.nominet.DDDS.DDDSinDNS.DNSRule#compareTo(uk.nominet.DDDS.DDDSinDNS.DNSRule)
*/
@Override
public int compareTo(DNSRule r) {
int n = super.compareTo(r);
if (n == 0 && r instanceof EnumRule) {
n = rank - ((EnumRule)r).rank;
}
return n;
}
}
}
/*
* Copyright 2009 Nominet UK
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.nominet.DDDS;
/**
* The generic representation of a rule in a DDDS database
*
* @author ray
*
*/
public interface Rule {
public int getPriority();
public String getFlags();
public String getService();
public boolean isTerminal();
public String evaluate();
}
......@@ -182,7 +182,16 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
},
function (err) {
self.trace('setLocalDescriptionOnFailure', err);
failureCallback(err);
if (err.indexOf("Called in wrong state") > -1)
{
setTimeout(function()
{
self.setLocalDescription(description, successCallback, failureCallback);
}, 1000);
} else failureCallback(err);
}
);
/*
......@@ -339,17 +348,6 @@ TraceablePeerConnection.prototype.modifySources = function(successCallback) {
// FIXME: this is a big hack
// https://code.google.com/p/webrtc/issues/detail?id=2688
// ^ has been fixed.
if (!(this.signalingState == 'stable' && this.iceConnectionState == 'connected')) {
console.warn('modifySources not yet', this.signalingState, this.iceConnectionState);
this.wait = true;
window.setTimeout(function() { self.modifySources(successCallback); }, 250);
return;
}
if (this.wait) {
window.setTimeout(function() { self.modifySources(successCallback); }, 2500);
this.wait = false;
return;
}
// Reset switch streams flag
this.switchstreams = false;
......
......@@ -338,7 +338,13 @@ Strophe.addConnectionPlugin('emuc', {
//console.log('Kick participant error: ', error);
});
},
sendPresence: function () {
sendPresence: function ()
{
if (!this.presMap['to']) {
// Too early to send presence - not initialized
return;
}
var pres = $pres({to: this.presMap['to'] });
pres.c('x', {xmlns: this.presMap['xns']});
......
......@@ -462,7 +462,7 @@ StatsCollector.prototype.processStatsReport = function () {
continue;
var jid = ssrc2jid[ssrc];
if (!jid) {
console.warn("No jid for ssrc: " + ssrc);
//console.warn("No jid for ssrc: " + ssrc);
continue;
}
......@@ -656,7 +656,7 @@ StatsCollector.prototype.processAudioLevelReport = function ()
var before = this.baselineAudioLevelsReport[idx];
if (!before)
{
console.warn(getStatValue(now, 'ssrc') + ' not enough data');
//console.warn(getStatValue(now, 'ssrc') + ' not enough data');
continue;
}
......@@ -664,7 +664,7 @@ StatsCollector.prototype.processAudioLevelReport = function ()
var jid = ssrc2jid[ssrc];
if (!jid)
{
console.warn("No jid for ssrc: " + ssrc);
//console.warn("No jid for ssrc: " + ssrc);
continue;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
(function() {
$(function() {
FastClick.attach(document.body);
return $(document).foundation();
});
}).call(this);
\ No newline at end of file
This diff is collapsed.
<%--
- $Revision$
- $Date$
-
- Copyright (C) 2004-2008 Jive Software. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--%>
<%@ page import="org.jivesoftware.openfire.plugin.ofmeet.*" %>
<%@ page import="org.jitsi.videobridge.openfire.*" %>
<%@ page import="org.jivesoftware.openfire.*" %>
<%@ page import="org.jivesoftware.util.*" %>
<%@ page import="org.jivesoftware.openfire.plugin.spark.Bookmark" %>
<%@ page import="org.jivesoftware.openfire.plugin.spark.BookmarkManager" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<%
String roomName = ParamUtils.getParameter(request,"name");
String roomId = ParamUtils.getParameter(request,"room");
String bookmarkId = ParamUtils.getParameter(request,"id");
String updatedEvents = ParamUtils.getParameter(request,"calendarevents");
String users = "[";
String groups = "[";
Bookmark bookmark = null;
try {
if (bookmarkId != null)
{
bookmark = BookmarkManager.getBookmark(Long.parseLong(bookmarkId));
if (updatedEvents != null)
{
bookmark.setProperty("calendar", updatedEvents);
OfMeetPlugin.self.processMeetingPlanner();
}
int i = 0;
int count = bookmark.getUsers().size();
int j = 0;
int count2 = bookmark.getGroups().size();
for (String user : bookmark.getUsers())
{
users = users + "'" + user + "'";
i++;
if (i < count) users = users + ",";
}
for (String group : bookmark.getGroups())
{
groups = groups + "'" + group + "'";
j++;
if (j < count2) groups = groups + ",";
}
}
users = users + "]";
groups = groups + "]";
} catch (Exception e) {}
%>
<html>
<head>
<title><fmt:message key="config.page.calendar.title" /></title>
<meta name="pageID" content="ofmeet-planner"/>
<link rel="stylesheet" href="vendor/font-awesome.min.css">
<link rel="stylesheet" href="vendor/jquery-ui.custom.min.css">
<link rel="stylesheet" href="vendor/fullcalendar.css"/>
<link rel="stylesheet" href="stylesheets/workshop_manager.css">
<link rel="stylesheet" href="vendor/foundation.css">
<script src="vendor/modernizr.js"></script>
<script src="vendor/moment.min.js"></script>
<script src="vendor/jquery-2.1.0.js"></script>
<script src="vendor/jquery-ui.custom.min.js"></script>
<script src="vendor/foundation.min.js"></script>
<script src="vendor/fullcalendar.js"></script>
<script src="vendor/jquery.ui.touch-punch.js"></script>
<script src="vendor/fastclick.js"></script>
<script src="javascripts/workshop_manager.js"></script>
<script src="javascripts/main.js"></script>
<script>
var roomName = "<%= roomName %>";
var roomId = "<%= roomId %>";
var users = <%= users %>;
var groups = <%= groups %>;
<%
String events = "[]";
if (bookmark != null)
{
events = bookmark.getProperty("calendar");
if (events == null) events = "[]";
}
%>
var DATA = {events: <%= events %>};
</script>
</head>
<body>
<h1><%= roomName %></h1>
<p>
<fmt:message key="ofmeet.calendar.description" />
</p>
<hr />
<div id="container" class="row">
<div id="workshop_manager" class="small-12 columns"></div>
</div>
<form action='ofmeet-calendar.jsp' id='calendarform' method='post'>
<input type='hidden' id='calendarevents' name='calendarevents'>
<input type='hidden' id='name' name='name' value='<%= roomName %>'>
<input type='hidden' id='room' name='room' value='<%= roomId %>'>
<input type='hidden' id='id' name='id' value='<%= bookmarkId %>'>
</form>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment