Commit be524c1f authored by Dele Olajide's avatar Dele Olajide Committed by dele

Implemented OF-703 (Auto-creation of shared groups in userservice plugin)

Demo application for Rayo plugin

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13752 b35dd754-fafc-0310-a699-88a17e54d16e
parent b7ac7a29
/*global io MediaServices Phono*/
(function () {
// Utils and references
var root = this,
att = {};
// global utils
var _ = att.util = {
_uuidCounter: 0,
uuid: function () {
return Math.random().toString(16).substring(2) + (_._uuidCounter++).toString(16);
},
slice: Array.prototype.slice,
isFunc: function (obj) {
return Object.prototype.toString.call(obj) == '[object Function]';
},
extend: function (obj) {
this.slice.call(arguments, 1).forEach(function (source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
},
each: function (obj, func) {
if (!obj) return;
if (obj instanceof Array) {
obj.forEach(func);
} else {
for (var key in obj) {
func(key, obj[key]);
}
}
},
getQueryParam: function (name) {
// query string parser
var cleaned = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"),
regexS = "[\\?&]" + cleaned + "=([^&#]*)",
regex = new RegExp(regexS),
results = regex.exec(window.location.search);
return (results) ? decodeURIComponent(results[1].replace(/\+/g, " ")) : undefined;
},
// used to try to determine whether they're using the ericsson leif browser
// this is not an ideal way to check, but I'm not sure how to do it since
// leif if pretty much just stock chromium.
h2sSupport: function () {
return !!window.webkitPeerConnection00 && window.navigator.userAgent.indexOf('Chrome/24') !== -1;
}
};
var phoneNumber = {};
phoneNumber.stringify = function (text) {
// strip all non numbers
var cleaned = phoneNumber.parse(text),
len = cleaned.length,
countryCode = (cleaned.charAt(0) === '1'),
arr = cleaned.split(''),
diff;
// if it's long just return it unformatted
if (len > (countryCode ? 11 : 10)) return cleaned;
// if it's too short to tell
if (!countryCode && len < 4) return cleaned;
// remove country code if we have it
if (countryCode) arr.splice(0, 1);
// the rules are different enough when we have
// country codes so we just split it out
if (countryCode) {
if (len > 1) {
diff = 4 - len;
diff = (diff > 0) ? diff : 0;
arr.splice(0, 0, " (");
// back fill with spaces
arr.splice(4, 0, (new Array(diff + 1).join(' ') + ") "));
if (len > 7) {
arr.splice(8, 0, '-');
}
}
} else {
if (len > 7) {
arr.splice(0, 0, "(");
arr.splice(4, 0, ") ");
arr.splice(8, 0, "-");
} else if (len > 3) {
arr.splice(3, 0, "-");
}
}
// join it back when we're done with the CC if it's there
return (countryCode ? '1' : '') + arr.join('');
};
phoneNumber.parse = function (input) {
return String(input)
.toUpperCase()
.replace(/[A-Z]/g, function (l) {
return (l.charCodeAt(0) - 65) / 3 + 2 - ("SVYZ".indexOf(l) > -1) | 0;
})
.replace(/\D/g, '');
};
phoneNumber.getCallable = function (input, countryAbr) {
var country = countryAbr || 'us',
cleaned = phoneNumber.parse(input);
if (cleaned.length === 10) {
if (country == 'us') {
return '1' + cleaned;
}
} else if (country == 'us' && cleaned.length === 11 && cleaned.charAt(0) === '1') {
return cleaned;
} else {
return false;
}
};
att.phoneNumber = phoneNumber;
// attach to window or export with commonJS
if (typeof exports !== 'undefined') {
module.exports = att;
} else {
// make sure we've got an "att" global
root.ATT || (root.ATT = {});
_.extend(root.ATT, att);
}
}).call(this);
// This code was written by Tyler Akins and has been placed in the
// public domain. It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com
var Base64 = (function () {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var obj = {
/**
* Encodes a string in base64
* @param {String} input The string to encode in base64.
*/
encode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
} while (i < input.length);
return output;
},
/**
* Decodes a base64 string.
* @param {String} input The string to decode.
*/
decode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
} while (i < input.length);
return output;
}
};
return obj;
})();
This diff is collapsed.
body {
position: relative;
-webkit-transition: top 1s; }
body.candybarVisible {
top: 100px; }
#callStatus {
position: fixed;
top: -120px;
left: 0px;
-webkit-transition: background-color 1s;
-webkit-transition: top 1s;
width: 100%;
height: 80px;
padding: 10px;
z-index: 1000; }
#callStatus.visible {
top: 0px; }
#callStatus.havatar .callActions {
left: 100px; }
#callStatus.havatar .caller {
margin-left: 90px; }
#callStatus.incoming {
background-color: #41ade0;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #63cfff;
border-bottom: 2px solid #00699c;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.incoming .callTime {
display: none; }
#callStatus.incoming .callerName:before {
content: "Incoming: "; }
#callStatus.waiting {
background-color: #f47820;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #ff9a42;
border-bottom: 2px solid #b03400;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.waiting .spinner div {
background-color: white; }
#callStatus.calling {
background-color: #41ade0;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #63cfff;
border-bottom: 2px solid #00699c;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.calling .callTime {
display: none; }
#callStatus.calling .callerName:before {
content: "Calling: "; }
#callStatus.active {
background-color: #77a803;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #99ca25;
border-bottom: 2px solid #336400;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.active .callerName:before {
content: "On Call: "; }
#callStatus.inactive {
background-color: white;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid white;
border-bottom: 2px solid #bbbbbb;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.remote {
background-color: #74e0ff;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #96ffff;
border-bottom: 2px solid #309cbb;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.remote .callTime {
display: none; }
#callStatus.ending {
background-color: #bbbbbb;
background-image: rgba(255, 255, 255, 0.3);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.3), rgba(50, 50, 50, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(50, 50, 50, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 2px solid #dddddd;
border-bottom: 2px solid #777777;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
box-shadow: rgba(0, 0, 0, 0.2) 0 3px 5px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.2),Direction=135,Strength=5); }
#callStatus.ending .callerName:before {
content: "Call ending with: "; }
#callStatus .callActions {
position: absolute;
left: 10px;
top: 50px;
display: block;
width: 100%; }
#callStatus nav {
float: left; }
#callStatus button {
min-width: auto;
background-color: rgba(255, 255, 255, 0.3);
background-image: rgba(255, 255, 255, 0.5);
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.5)), color-stop(1, rgba(0, 0, 0, 0.1)));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
border-top: 1px solid rgba(255, 255, 255, 0.6);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
border-left: 1px solid rgba(255, 255, 255, 0.2);
border-right: 1px solid rgba(255, 255, 255, 0.2);
width: 100px;
margin-right: 10px;
font-size: 16px;
color: rgba(0, 0, 0, 0.75);
text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(255, 255, 255, 0.5),Direction=135,Strength=0);
float: left;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
box-shadow: rgba(0, 0, 0, 0.3) 0 1px 3px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.3),Direction=135,Strength=3); }
#callStatus button:hover {
background-color: rgba(255, 255, 255, 0.4); }
#callStatus button:active {
box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 3px;
-moz-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 3px;
-webkit-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 3px;
padding-top: 11px;
padding-bottom: 9px;
border-bottom: 1px solid white;
border-top: 1px solid rgba(0, 0, 0, 0.2); }
#callStatus .callerAvatar {
float: left;
width: 65px;
height: 65px;
border: 5px solid #eeeeee;
margin-right: 10px; }
#callStatus .callerName, #callStatus .callTime {
font-weight: bold;
color: white;
text-shadow: rgba(0, 0, 0, 0.7) 0 1px 0px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=rgba(0, 0, 0, 0.7),Direction=135,Strength=0);
line-height: 1; }
#callStatus .caller {
margin-top: 0px;
margin-right: 30px;
margin-left: 0px;
font-size: 20px;
padding-bottom: 0px;
border-bottom: 2px groove rgba(255, 255, 255, 0.4); }
#callStatus .callerName {
display: inline; }
#callStatus .callerNumber {
display: inline;
margin-left: 10px; }
#callStatus .callTime {
position: absolute;
top: 12px;
right: 40px;
font-size: 20px;
margin: 0; }
This diff is collapsed.
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section {
display: block; }
audio, canvas, video {
display: inline-block;
*display: inline;
*zoom: 1; }
audio:not([controls]) {
display: none; }
[hidden] {
display: none; }
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%; }
html, button, input, select, textarea {
font-family: sans-serif;
color: #222222; }
body {
margin: 0;
font-size: 1em;
line-height: 1.4; }
a {
color: #0000ee; }
a:visited {
color: #551a8b; }
a:hover {
color: #0066ee; }
a:focus {
outline: thin dotted; }
a:hover, a:active {
outline: 0; }
abbr[title] {
border-bottom: 1px dotted; }
b, strong {
font-weight: bold; }
blockquote {
margin: 1em 40px; }
dfn {
font-style: italic; }
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #cccccc;
margin: 1em 0;
padding: 0; }
ins {
background: #ffff99;
color: black;
text-decoration: none; }
mark {
background: yellow;
color: black;
font-style: italic;
font-weight: bold; }
pre, code, kbd, samp {
font-family: monospace, serif;
_font-family: "courier new", monospace;
font-size: 1em; }
pre {
white-space: pre-wrap;
word-wrap: break-word; }
q {
quotes: none; }
q:before, q:after {
content: none; }
small {
font-size: 85%; }
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline; }
sup {
top: -0.5em; }
sub {
bottom: -0.25em; }
ul, ol {
margin: 1em 0;
padding: 0 0 0 40px; }
dd {
margin: 0 0 0 40px; }
nav ul, nav ol {
list-style: none;
list-style-image: none;
margin: 0;
padding: 0; }
img {
border: 0;
-ms-interpolation-mode: bicubic;
vertical-align: middle; }
svg:not(:root) {
overflow: hidden; }
figure {
margin: 0; }
form {
margin: 0; }
fieldset {
border: 0;
margin: 0;
padding: 0; }
label {
cursor: pointer; }
legend {
border: 0;
*margin-left: -7px;
padding: 0;
white-space: normal; }
button, input, select, textarea {
font-size: 100%;
margin: 0;
vertical-align: baseline;
*vertical-align: middle; }
button, input {
line-height: normal; }
button, input[type="button"], input[type="reset"], input[type="submit"] {
cursor: pointer;
-webkit-appearance: button;
*overflow: visible; }
button[disabled], input[disabled] {
cursor: default; }
input[type="checkbox"], input[type="radio"] {
box-sizing: border-box;
padding: 0;
*width: 13px;
*height: 13px; }
input[type="search"] {
-webkit-appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box; }
input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none; }
button::-moz-focus-inner, input::-moz-focus-inner {
border: 0;
padding: 0; }
textarea {
overflow: auto;
vertical-align: top;
resize: vertical; }
input:invalid, textarea:invalid {
background-color: #f0dddd; }
table {
border-collapse: collapse;
border-spacing: 0; }
td {
vertical-align: top; }
.clearfix:before, .clearfix:after {
content: "\0020";
display: block;
height: 0;
visibility: hidden; }
.clearfix:after {
clear: both; }
.clearfix {
zoom: 1; }
body {
font-family: sans-serif; }
.dialerwrapper {
width: 280px;
height: 350px;
background-color: #cccccc; }
.dialerwrapper .numberEntry {
width: 100%;
height: 15%;
text-align: center;
text-shadow: 1px 1px 0 #555555;
font-size: 2em;
color: white;
outline: 0;
border: 0;
background: #959595;
background: -moz-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(50%, #959595), color-stop(50%, #767676), color-stop(100%, #565656));
background: -webkit-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: -o-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: -ms-linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
background: linear-gradient(top, #959595 50%, #767676 50%, #565656 100%);
padding: 0;
margin: 0;
border-top: 1px solid #777777; }
.dialerwrapper .numberEntry:focus {
outline: 0; }
.dialerwrapper #dialpad {
width: 100%;
height: 100%;
list-style: none;
margin: 0;
padding: 0; }
.dialerwrapper #dialpad li {
width: inherit;
height: 21.25%;
white-space: nowrap;
font-size: 0;
margin: 0;
padding: 0; }
.dialerwrapper #dialpad button {
display: inline-block;
vertical-align: top;
width: 33.3%;
height: 100%;
background: #eeeeee;
font-size: 20px;
text-align: center;
outline: 0;
border-top: 1px solid white;
border-right: 1px solid white;
border-bottom: 1px solid #c7c7c7;
border-left: 1px solid #c7c7c7;
border-radius: 0;
margin: 0;
padding: 0;
color: #555555;
vertical-align: middle; }
.dialerwrapper #dialpad button:hover {
background-color: #cecece; }
.dialerwrapper #dialpad button p {
width: 100%;
display: inline-table;
font-size: 1.2em;
font-weight: 700;
margin: 0; }
.dialerwrapper #dialpad button div {
text-transform: uppercase;
font-size: 0.6em; }
.dialerwrapper #actions nav {
position: relative; }
.dialerwrapper #actions a {
width: 33.3%; }
.close_dialer {
background-color: #555555;
height: 25px;
width: 100%;
display: block;
position: relative; }
.cancel {
position: absolute;
top: 4px;
right: 10px;
padding: 3px;
line-height: 9px;
display: block;
color: white;
border: 1px solid white;
background-color: #666666; }
.cancel:hover {
color: white;
background-color: #bb0000;
cursor: pointer; }
.call {
cursor: hand;
}
#screen {
overflow: hidden;
padding: 15px 15px 0 15px;
z-index: 99; }
#screen header {
background-color: #555555; }
#screen header, #screen footer {
height: 40px;
width: 100%;
padding: 10px 0;
display: block; }
#screen header nav, #screen footer nav {
width: 100%;
display: table;
margin: 0 auto;
border: 1px solid #222222;
border-radius: 10px; }
#screen header nav a, #screen footer nav a {
display: table-cell;
width: 50%;
color: #eeeeee;
font-weight: 900;
text-decoration: none;
text-align: center;
text-shadow: 1px 1px 0 #222222;
letter-spacing: 0.3px;
padding: 10px 0;
border-left: 1px solid #222222;
border-right: 1px solid #777777;
background: #888888;
background: -moz-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #888888) repeat-x, color-stop(100%, #555555)), #888888;
background: -webkit-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: -o-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: -ms-linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
background: linear-gradient(top, #888888 0%, #555555 100%) repeat-x, #888888;
-moz-transition: background 1s linear;
-webkit-transition: all 0.3s linear 0;
-moz-transition-property: all;
-moz-transition-duration: 1s;
-moz-transition-timing-function: linear;
-moz-transition-delay: linear; }
#screen header nav a:first-child, #screen footer nav a:first-child {
border-left: 0;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px; }
#screen header nav a:last-child, #screen footer nav a:last-child {
margin: 0;
border-right: 0;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px; }
#screen header nav a:hover, #screen footer nav a:hover {
background-position: 0 15px;
cursor: pointer; }
#login #screen {
padding: 0;
margin: 0 10px 10px 10px;
position: relative; }
#login #screen:after {
content: "";
width: 280px;
height: 1px;
display: block;
position: absolute;
left: 1px;
bottom: 0px;
background: #cccccc;
z-index: 200000000; }
#login footer {
display: none; }
#login .dialerwrapper {
background-color: white;
border-right: 1px solid #cccccc; }
#login .numberEntry {
border-left: 0px solid #dddddd;
margin-left: 1px;
margin-right: 2px;
padding-top: 3px;
border-top: 1px solid #dddddd;
background-image: #eeeeee;
background-image: -moz-linear-gradient(top, #eeeeee, #dddddd);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #eeeeee), color-stop(1, #dddddd));
-ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='$top', EndColorStr='$bottom');
color: #555555;
text-shadow: white 0 1px 0px;
-ms-filter: progid:DXImageTransform.Microsoft.Shadow(Color=white,Direction=135,Strength=0); }
This diff is collapsed.
<html>
<head>
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" href="dialpad.css">
<link rel="stylesheet" href="candybar.css">
<script src="jquery_1_4_2.js"></script>
<script src="md5.js"></script>
<script src="base64.js"></script>
<script src="strophe.js"></script>
<script src="strophe-openfire.js"></script>
<script src="rayo-plugin.js"></script>
<script src="candybar.js"></script>
<script src="att.phonenumber.js"></script>
<script src="dialpad.js"></script>
<script>
var avatar = 'unknown.jpg';
var callerId = "unknown";
var domain = "81.201.82.25";
var prefix = "sip:";
var handsetOffhook = false;
var ringtone;
window.dialer = new Dialpad({
onPress: function (key) {
console.log('a key was pressed', key);
if (window.candybar.call) window.candybar.call.digit(key);
},
onCallableNumber: function (number) {
console.log('we have a number that seems callable', number);
//makeCall(number);
},
onHide: function () {
console.log('removed it');
},
onCall: function (number) {
if (window.dialer.getCallLabel() == "Call") {
console.log('The call button was pressed', number);
makeCall(number);
} else if (window.dialer.getCallLabel() == "Hangup") {
window.candybar.call.hangup();
} else {
window.candybar.call.answer();
}
}
});
window.candybar = new CandyBar();
$(document).ready(function()
{
document.getElementById("dialpadDiv").insertBefore(window.dialer.render());
window.candybar.render();
window.candybar.call = null;
if (urlParam("prefix")) prefix = urlParam("prefix");
if (urlParam("domain")) domain = urlParam("domain");
if (domain == "81.201.82.25" && prefix == "sip:") prefix = "sip:883510";
if (prefix == "tel:") domain = "";
var iNum = urlParam("inum");
if (!iNum)
{
iNum = Math.random().toString(36).substr(2,9);
}
window.connection = new Openfire.Connection(window.location.protocol + '//' + window.location.host + '/http-bind/');
window.connection.resource = iNum;
window.connection.addHandler(handlePresence, null,"presence", null, null, null);
window.connection.connect(window.location.hostname, null, function (status)
{
//console.log("XMPPConnection.connect");
//console.log(status);
if (status === Strophe.Status.CONNECTED)
{
$("#status").html("Ready");
setPresence();
$(window).unload( function() {
onhook();
window.connection.disconnect();
});
setPresence();
getContacts();
offhook();
}
});
var destination = urlParam("destination");
if (destination)
{
makeCall(destination);
}
})
function setPresence(chat)
{
//console.log("setPresence");
if (window.connection)
{
var presence = $pres();
if (chat) presence.c('show').t(chat).up();
window.connection.send(presence);
}
}
function offhook()
{
console.log("offhook()");
if (window.connection)
{
window.connection.rayo.offhook(
{
codec: "OPUS",
stereo: "0",
onReady: function() {
console.log('Handset is off hook');
$("#status").html("Off Hook");
handsetOffhook = true;
},
onUnready: function() {
console.log('Handset is on hook');
$("#status").html("On Hook");
handsetOffhook = false;
},
onEnd: function() {
console.log('Handset is disconnected');
$("#status").html("On Hook");
handsetOffhook = false;
},
onError: function(e) {
console.error(e);
}
});
}
}
function toggleHook()
{
console.log("onhook()");
if (window.connection)
{
if (handsetOffhook)
window.connection.rayo.onhook();
else
offhook()
}
}
function handlePresence(presence)
{
//console.log("handlePresence");
//console.log(presence);
var from = $(presence).attr('from');
var iNum = Strophe.getResourceFromJid(from);
var xquery = presence.getElementsByTagName("x");
if (xquery.length == 0)
{
var type = $(presence).attr('type');
if (type == "unavailable")
{
} else {
//var status = $(presence).find('status').text();
}
}
return true;
};
function getContacts ()
{
//console.log("getContacts ");
};
function makeCall(destination)
{
console.log("makeCall " + destination);
var sipUri = prefix + destination + "@" + domain
if (prefix == "tel:") sipUri = prefix + destination
window.candybar.call = window.connection.rayo.dial(
{
from: 'unknown',
to: sipUri,
onEnd: function() {
//console.log('ended...............');
window.candybar.endGently();
window.candybar.call = null;
window.dialer.setCallLabel('Call');
},
onAnswer: function() {
//console.log('answered...............');
window.candybar.setState('active');
window.dialer.setCallLabel('Hangup');
stopTone();
},
onRing: function() {
//console.log('ringing...............');
window.candybar.setState('calling');
startTone("ringback-uk");
},
onError: function(e) {
//console.log('dial error ' + e);
window.candybar.endGently();
window.candybar.call = null;
}
});
window.candybar.setUser({
name: callerId,
number: destination,
picUrl: 'unknown.jpg'
});
}
function onIncomingCall(event)
{
console.log(' call from ' + event.call.initiator);
if (window.candybar.call == null) // ignore when user has active call
{
var destination = Strophe.getNodeFromJid(event.call.initiator);
window.candybar.setUser({
name: destination,
number: destination,
picUrl: 'http://placekitten.com/100/100'
});
window.candybar.call = event.call;
window.candybar.setState('incoming');
window.dialer.setCallLabel('Answer');
/*
window.candybar.call.bind(
{
onHangup: function(event)
{
window.candybar.endGently();
//window.candybar.call = null;
window.dialer.setCallLabel('Call');
},
onAnswer: function(event)
{
window.candybar.setState('active');
window.dialer.setCallLabel('Hangup');
},
onError: function(event)
{
console.log('call error ' + event.reason);
}
});
*/
}
}
function urlParam(name)
{
var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (!results) { return undefined; }
return decodeURIComponent(results[1]) || undefined;
}
function startTone(name)
{
ringtone = new Audio();
ringtone.loop = true;
ringtone.src = "ringtones/" + name + ".mp3";
ringtone.play();
}
function stopTone()
{
ringtone.pause();
ringtone = null;
}
</script>
</head>
<body>
<div id="dialpadDiv" style="position: absolute; width: 500px: height: 300px;"/>
<span id="status" onClick="toggleHook()">Loading...</span>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -46,13 +46,17 @@ public class Handset extends BaseVerb {
@NotNull(message=Handset.MISSING_STEREO)
public String stereo;
public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo)
@NotNull(message=Handset.MISSING_MIXER)
public String mixer;
public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo, String mixer)
{
this.cryptoSuite = cryptoSuite;
this.localCrypto = localCrypto;
this.remoteCrypto = remoteCrypto;
this.codec = codec;
this.stereo = stereo;
this.mixer = mixer;
}
@Override
......@@ -66,6 +70,7 @@ public class Handset extends BaseVerb {
.append("remoteCrypto",remoteCrypto)
.append("codec",codec)
.append("stereo",stereo)
.append("stereo",mixer)
.toString();
}
}
......@@ -2,4 +2,13 @@ package com.rayo.core.verb;
public class OffHookCommand extends AbstractVerbCommand {
private Handset handset;
public void setHandset(Handset handset) {
this.handset = handset;
}
public Handset getHandset() {
return this.handset;
}
}
......@@ -43,10 +43,7 @@ public class HandsetProvider extends BaseProvider {
@Override
protected Object processElement(Element element) throws Exception
{
if (element.getName().equals("handset")) {
return buildHandset(element);
} else if (ONHOOK_QNAME.equals(element.getQName())) {
if (ONHOOK_QNAME.equals(element.getQName())) {
return buildOnHookCommand(element);
} else if (OFFHOOK_QNAME.equals(element.getQName())) {
......@@ -69,34 +66,33 @@ public class HandsetProvider extends BaseProvider {
return complete;
}
private Object buildHandset(Element element) throws URISyntaxException
{
Handset handset = new Handset( element.attributeValue("cryptoSuite"),
element.attributeValue("localCrypto"),
element.attributeValue("remoteCrypto"),
private Object buildOffHookCommand(Element element) throws URISyntaxException {
Handset handset = new Handset( element.attributeValue("cryptosuite"),
element.attributeValue("localcrypto"),
element.attributeValue("remotecrypto"),
element.attributeValue("codec"),
element.attributeValue("stereo"));
return handset;
element.attributeValue("stereo"),
element.attributeValue("mixer"));
OffHookCommand command = new OffHookCommand();
command.setHandset(handset);
return command;
}
private Object buildOnHookCommand(Element element) throws URISyntaxException {
return new OnHookCommand();
}
private Object buildOffHookCommand(Element element) throws URISyntaxException {
return new OffHookCommand();
}
// Object -> XML
// ================================================================================
@Override
protected void generateDocument(Object object, Document document) throws Exception {
if (object instanceof Handset) {
createHandset((Handset) object, document);
} else if (object instanceof OnHookCommand) {
if (object instanceof OnHookCommand) {
createOnHookCommand((OnHookCommand) object, document);
} else if (object instanceof OffHookCommand) {
......@@ -107,24 +103,23 @@ public class HandsetProvider extends BaseProvider {
}
}
private void createHandset(Handset handset, Document document) throws Exception {
Element root = document.addElement(new QName("handset", NAMESPACE));
private void createOffHookCommand(OffHookCommand command, Document document) throws Exception {
Handset handset = command.getHandset();
Element root = document.addElement(new QName("offhook", NAMESPACE));
root.addAttribute("cryptoSuite", handset.cryptoSuite);
root.addAttribute("localCrypto", handset.localCrypto);
root.addAttribute("remoteCrypto", handset.cryptoSuite);
root.addAttribute("codec", handset.codec);
root.addAttribute("stereo", handset.stereo);
root.addAttribute("mixer", handset.mixer);
}
private void createOnHookCommand(OnHookCommand command, Document document) throws Exception {
document.addElement(new QName("onhook", NAMESPACE));
}
private void createOffHookCommand(OffHookCommand command, Document document) throws Exception {
document.addElement(new QName("offhook", NAMESPACE));
}
private void createHandsetCompleteEvent(SayCompleteEvent event, Document document) throws Exception {
addCompleteElement(document, event, COMPLETE_NAMESPACE);
}
......
......@@ -338,11 +338,11 @@ public abstract class CallHandler extends Thread {
}
if (otherCall != null) {
Logger.println("Call " + cp + " forwarding dtmf key "
+ dtmfKeys + " to " + otherCall);
otherCall.getMemberSender().setDtmfKeyToSend(dtmfKeys);
}
Logger.println("Call " + cp + " forwarding dtmf key " + dtmfKeys + " to " + otherCall);
otherCall.getMemberSender().setDtmfKeyToSend(dtmfKeys);
} else {
getMemberSender().setDtmfKeyToSend(dtmfKeys);
}
} else {
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println(cp + " Call not established, ignoring dtmf");
......@@ -642,6 +642,30 @@ public abstract class CallHandler extends Thread {
cancel(callsToCancel, reason, false);
}
public static void hangupOwner(String ownerId, String reason) {
Vector callsToCancel = new Vector();
synchronized(activeCalls) {
/*
* Make a list of all the calls we want to cancel, then cancel them.
* We have to cancel them while not synchronized or
* we could deadlock.
*/
for (int i = 0; i < activeCalls.size(); i++) {
CallHandler call = (CallHandler)activeCalls.elementAt(i);
CallParticipant cp = call.getCallParticipant();
if (cp.getCallOwner().equals(ownerId)) {
callsToCancel.add(call);
}
}
}
cancel(callsToCancel, reason, false);
}
public static void suspendBridge() {
cancel(activeCalls, "bridge suspended", true);
}
......
......@@ -684,7 +684,7 @@ public class ConferenceManager {
leave(member, true); // leave the temporary conference
member.reinitialize(newConferenceManager);
member.reinitialize(newConferenceManager, false);
newConferenceManager.joinConference(member); // join the new conference
}
......
......@@ -584,6 +584,11 @@ public class ConferenceMember implements TreatmentDoneListener,
}
public void reinitialize(ConferenceManager conferenceManager) {
reinitialize(conferenceManager, true);
}
public void reinitialize(ConferenceManager conferenceManager, boolean initialize) {
synchronized (conferenceManager) {
Logger.println("Call " + this + " Reinitializing");
......@@ -615,10 +620,11 @@ public class ConferenceMember implements TreatmentDoneListener,
* When the call is transferred to the actual conference,
* the conference parameters may be different.
*/
initialize(callHandler, memberSender.getSendAddress(),
memberSender.getMediaInfo().getPayload(),
memberReceiver.getMediaInfo().getPayload(),
(byte) memberReceiver.getTelephoneEventPayload(), rtcpAddress);
if (initialize)
{
initialize(callHandler, memberSender.getSendAddress(), memberSender.getMediaInfo().getPayload(), memberReceiver.getMediaInfo().getPayload(), (byte) memberReceiver.getTelephoneEventPayload(), rtcpAddress);
}
}
}
conferenceManager.joinDistributedConference(this);
......
......@@ -515,7 +515,7 @@ public class IncomingCallHandler extends CallHandler
return ((IncomingCallHandler)callHandler).transferCall(conferenceId);
}
private static ConferenceManager transferCall(CallHandler callHandler, String conferenceId) throws IOException
public static ConferenceManager transferCall(CallHandler callHandler, String conferenceId) throws IOException
{
/*
* Get current conference manager and member.
......
......@@ -53,6 +53,9 @@ import com.rayo.core.verb.*;
import org.voicebridge.*;
import com.sun.voip.server.*;
import com.sun.voip.*;
public class RayoPlugin implements Plugin, SessionEventListener {
......@@ -331,6 +334,8 @@ public class RayoPlugin implements Plugin, SessionEventListener {
public void anonymousSessionDestroyed(Session session)
{
Log.debug("RayoPlugin anonymousSessionDestroyed "+ session.getAddress().toString() + "\n" + ((ClientSession) session).getPresence().toXML());
CallHandler.hangupOwner(session.getAddress().toString(), "User has ended session");
}
public void resourceBound(Session session)
......@@ -347,5 +352,6 @@ public class RayoPlugin implements Plugin, SessionEventListener {
{
Log.debug("RayoPlugin sessionDestroyed "+ session.getAddress().toString() + "\n" + ((ClientSession) session).getPresence().toXML());
CallHandler.hangupOwner(session.getAddress().toString(), "User has ended session");
}
}
......@@ -297,6 +297,33 @@ public class Config implements MUCEventListener {
}
}
public static void recordCall(String username, String addressFrom, String addressTo, long datetime, int duration, String calltype) {
String sql = "INSERT INTO ofSipPhoneLog (username, addressFrom, addressTo, datetime, duration, calltype) values (?, ?, ?, ?, ?, ?)";
Connection con = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
psmt = con.prepareStatement(sql);
psmt.setString(1, username);
psmt.setString(2, addressFrom);
psmt.setString(3, addressTo);
psmt.setLong(4, datetime);
psmt.setInt(5, duration);
psmt.setString(6, calltype);
psmt.executeUpdate();
} catch (SQLException e) {
Log.debug(e.getMessage(), e);
} finally {
DbConnectionManager.closeConnection(rs, psmt, con);
}
}
public ProxyCredentials getProxyCredentialsByUser(String username)
{
......
......@@ -249,6 +249,10 @@ public class RelayChannel {
return callHandler;
}
public void setCallHandler(OutgoingCallHandler callHandler) {
this.callHandler = callHandler;
}
public Handset getHandset() {
return handset;
}
......
......@@ -26,7 +26,7 @@
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
......@@ -43,6 +43,12 @@
<h1>
User Service Plugin Changelog
</h1>
<p><b>1.4.1</b> -- September ??th, 2013</p>
<ul>
<li>Added ability to create new shared groups from list of groups when adding or updating users to roster</li>
</ul>
<p><b>1.4.0</b> -- Sep 13, 2013</p>
<ul>
<li>Requires Openfire 3.9.0.</li>
......
......@@ -5,7 +5,7 @@
<name>User Service</name>
<description>Allows administration of users via HTTP requests.</description>
<author>Justin Hunt</author>
<version>1.4.0</version>
<version>1.4.1</version>
<date>09/13/2013</date>
<minServerVersion>3.9.0</minServerVersion>
......
......@@ -58,7 +58,7 @@ User Service Plugin Readme
<p>
The User Service Plugin provides the ability to add,edit,delete users and manage their rosters by sending an http request to the server.
It is intended to be used by applications automating the user administration process.
It is intended to be used by applications automating the user administration process.
This plugin's functionality is useful for applications that need to administer users outside of the Openfire admin console.
An example of such an application might be a live sports reporting application that uses XMPP as its transport, and
creates/deletes users according to the receipt, or non receipt, of a subscription fee.
......@@ -73,16 +73,16 @@ userservice.jar file over the existing file.</p>
<h2>Configuration</h2>
Access to the service is restricted with a "secret" that can be viewed and
Access to the service is restricted with a "secret" that can be viewed and
set from the User Service page in the Openfire admin console. This page is
located on the admin console under "Server" and then "Server Settings".
This should really only be considered weak security. The plugin was initially written with the assumption that http access to the Openfire service was
only available to trusted machines. In the case of the plugin's author, a web application running on the same server as
only available to trusted machines. In the case of the plugin's author, a web application running on the same server as
Openfire makes the request.
<h2>Using the Plugin</h2>
To administer users, submit HTTP requests to the userservice service.
To administer users, submit HTTP requests to the userservice service.
The service address is [hostname]plugins/userService/userservice. For example,
if your server name is "example.com", the URL is http://example.com/plugins/userService/userservice<p>
......@@ -94,7 +94,7 @@ The following parameters can be passed into the request:<p>
<th colspan=2>Name</th><th>Description</th>
</tr>
<tr>
<td class="name">type</td><td>Required</td><td>The admin service required.
<td class="name">type</td><td>Required</td><td>The admin service required.
Possible values are 'add', 'delete', 'update', 'enable', 'disable', 'add_roster', 'update_roster', 'delete_roster'.</td>
</tr>
<tr>
......@@ -119,7 +119,10 @@ The following parameters can be passed into the request:<p>
</tr>
<tr>
<td class="name">groups</td><td>Optional</td>
<td>List of groups where the user is a member. Values are comma delimited.</td>
<td>
List of groups where the user is a member. Values are comma delimited. When used with types "add" or "update", it adds the user to shared groups and auto-creates new groups.<br/>
When used with 'add_roster' and 'update_roster', it adds the user to roster groups provided the group name does not clash with an existing shared group.
</td>
</tr>
<tr>
<td class="name">item_jid</td><td>Required for 'add_roster', 'update_roster', 'delete_roster' operations.</td>
......@@ -146,6 +149,16 @@ http://example.com:9090/plugins/userService/userservice?type=add&secret=bigsecre
</form>
</ul>
The following example adds a user, adds two shared groups (if not existing) and adds the user to both groups.
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=add&secret=bigsecret&username=kafka&password=drowssap&name=franz&email=franz@kafka.com&groups=support,finance
</textarea>
</form>
</ul>
The following example deletes a user
<ul>
......@@ -197,6 +210,16 @@ http://example.com:9090/plugins/userService/userservice?type=add_roster&secret=b
</form>
</ul>
The following example adds new roster item with subscription 'both' for user 'kafka' and adds kafka to roster groups 'family' and 'friends'
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=add_roster&secret=bigsecret&username=kafka&item_jid=franz@example.com&name=franz&subscription=3&groups=family,friends
</textarea>
</form>
</ul>
The following example updates existing roster item to subscription 'none' for user 'kafka'
<ul>
......@@ -218,15 +241,15 @@ http://example.com:9090/plugins/userService/userservice?type=delete_roster&secre
</ul>
<br><br>
* When sending double characters (Chinese/Japanese/Korean etc) you should URLEncode the string as utf8.<br>
In Java this is done like this<br>
* When sending double characters (Chinese/Japanese/Korean etc) you should URLEncode the string as utf8.<br>
In Java this is done like this<br>
URLEncoder.encode(username, "UTF-8"));
<br>If the strings are encoded incorrectly, double byte characters will look garbeled in the Admin Console.
<h2>Server Reply</h2>
The server will reply to all User Service requests with an XML result page.
The server will reply to all User Service requests with an XML result page.
If the request was processed successfully the return will be a "result" element with a text body of "OK".
If the request was unsuccessful, the return will be an "error" element with a text body of one of the following error strings.
<p>
......
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