Skip to content

Instantly share code, notes, and snippets.

@waqaskhan137
Created January 23, 2017 07:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save waqaskhan137/0950232d4dc9645c4c3992d830f9bf1f to your computer and use it in GitHub Desktop.
Save waqaskhan137/0950232d4dc9645c4c3992d830f9bf1f to your computer and use it in GitHub Desktop.
var finesse = finesse || {};
finesse.gadget = finesse.gadget || {};
finesse.container = finesse.container || {};
// Gadget Config needed for instantiating ClientServices
/** @namespace */
finesse.gadget.Config = (function () {
var _prefs = new gadgets.Prefs();
/** @scope finesse.gadget.Config */
return {
authorization: _prefs.getString("authorization"),
country: _prefs.getString("country"),
language: _prefs.getString("language"),
locale: _prefs.getString("locale"),
host: _prefs.getString("host"),
hostPort: _prefs.getString("hostPort"),
extension: _prefs.getString("extension"),
mobileAgentMode: _prefs.getString("mobileAgentMode"),
mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
xmppDomain: _prefs.getString("xmppDomain"),
pubsubDomain: _prefs.getString("pubsubDomain"),
restHost: _prefs.getString("restHost"),
scheme: _prefs.getString("scheme"),
localhostFQDN: _prefs.getString("localhostFQDN"),
localhostPort: _prefs.getString("localhostPort"),
teamId: _prefs.getString("teamId"),
teamName: _prefs.getString("teamName"),
clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
compatibilityMode: _prefs.getString("compatibilityMode")
};
}());
/** @namespace */
finesse.modules = finesse.modules || {};
finesse.modules.SparkTeamAnnouncementsGadget = (function ($) {
var clientLogs = finesse.cslogger.ClientLogger; // for logging
var gadgetParams = {gadgetTitle:"Spark Team Announcements", sparkRoom:"Spark Team Announcements", maxMessages:"5", token: "", lastMsgTimestamp:0}; // Default values
var user;
var imagesPrefixURL;
var roomMembershipMap = [];
var forceGadgetUpdate = false;
/* Finesse Container Timer Variables */
var _lastProcessedTimerTick = null;
var _maxTimerCallbackThreshold = 1500; // Timer is called back every 1.5 seconds
var _forceTickProcessingEvery = 15000; // Timer for querying Spark every 15 seconds
///////////////////// FILL OUT THIS SECTION /////////////////////
var finesseDomain = "192.168.1.61"; // Fill in your Finesse domain
// Fill in the OAuth Authorization URI from the Spark Integration Details page
var OAuthAuthorizationURL = "https://api.ciscospark.com/v1/authorize?client_id=C2cdaaecafa2126ee7d24d3bdf37f4cecfe988d7694af2f558ca3060915ced930&response_type=code&redirect_uri=http%3A%2F%2F192.168.1.88%3A7070%2Foauth.html&scope=spark%3Amessages_write%20spark%3Arooms_read%20spark%3Ateams_read%20spark%3Amessages_read%20spark%3Arooms_write%20spark%3Apeople_read%20spark%3Akms%20spark%3Ateams_write&state=set_state_here";
////////////////////////////////////////////////////////////////
/**
* Using the URL from the Gadget iFrame, find the URL param, and attempt to find URL params set on that URL
*/
_initializeGadgetURLParams = function () {
clientLogs.log("_initializeGadgetURLParams()");
var iFrameURLParams = document.location.href.split("?")[1].split("&");
clientLogs.log("_initializeGadgetURLParams() - iFrameURLParams: " + iFrameURLParams);
// Save the up_host
var upLocalhost;
for (i = 0; i < iFrameURLParams.length; i++) {
iFrameURLParam = iFrameURLParams[i];
var URLparamKey = iFrameURLParam.split("=")[0];
if (URLparamKey === "url") {
var gadgetURL = decodeURIComponent(iFrameURLParam.split("=")[1].split("#")[0]);
clientLogs.log("_initializeGadgetURLParams() - gadgetURL: " + gadgetURL);
var gadgetFolder = gadgetURL
if (gadgetURL.split("?").length > 1) {
var gadgetURLParams = gadgetURL.split("?")[1];
clientLogs.log("_initializeGadgetURLParams() - gadgetURLParams: " + gadgetURLParams);
gadgetURLParams = gadgetURLParams.split("&");
for (j = 0; j < gadgetURLParams.length; j++) {
gadgetURLParam = gadgetURLParams[j];
gadgetParams[gadgetURLParam.split("=")[0]] = decodeURIComponent(gadgetURLParam.split("=")[1]);
}
}
// Parse the url to get the prefix path for the images
imagesPrefixURL = gadgetURL.substring(0, gadgetURL.lastIndexOf("/"));
} else if (URLparamKey === "up_localhostFQDN") {
upLocalhost = iFrameURLParam.split("=")[1];
}
}
// Replace "localhost" with the upHost
if(imagesPrefixURL.indexOf("localhost") != -1) {
// Replace the localhost with the fqdn
imagesPrefixURL = imagesPrefixURL.replace("localhost", upLocalhost);
}
clientLogs.log("_initializeGadgetURLParams() - imagesPrefixURL: " + imagesPrefixURL);
},
/**
* Take a single message (Announcement) and build the HTML to put it in the container
* in the appropriate format.
*
* - If the personData is not yet available, it will show a default avatar and "Unknown" as the name
* - Messages that were posted < 60 seconds are highlighted in blue
*
* Adjust the height of the gadget to fit the messages.
*/
handleRepaintGadget = function (item, personData, currTime, monthNames) {
clientLogs.log("handleRepaintGadget() - message is: " + item.text);
var gadgetContent = '';
var created = "";
// Figure out the time for the message
var msgDate = new Date(item.created);
var msgTime = msgDate.getTime();
var sec = ((currTime - msgTime) / 1000); //How long has it been since this message arrived
var min = Math.round(sec / 60);
var hou = Math.round(min / 60);
if (sec < 60) created = "Just Now";
else if (min < 2) created = min + " minute ago";
else if (min < 60) created = min + " minutes ago";
else if (hou < 2) created = hou + " hour ago";
else if (hou < 12) created = hou + " hours ago";
else created = monthNames[msgDate.getMonth()] + " " + msgDate.getDate() + ", " + msgDate. getFullYear();
// Figure out the avatar and name for the message.
// If the people details API has not completed yet, just show default information that will be updated later.
var personImage = imagesPrefixURL + "/images/Default-Avatar.png"; // Default avatar
var personName = "Unknown";
if (personData) {
if (personData.avatar) personImage = personData.avatar;
personName = personData.displayName;
}
// If the Annoucement is new, ensure that it is displayed with a blue background else a white background
var messageBackgroundColor = "#FFFFFF";
if (sec < 60) messageBackgroundColor = "#E0FFFF";
// Build the message HTML
gadgetContent += '<tr style="outline: 1px solid #B5B5B5; background-color: ' + messageBackgroundColor + '"class="autoRow">';
// Figure out the message
var message = item.text;
if (message) {
// Special formatting for important messages
if (message.indexOf("#IMPORTANT") === 0) {
// Strip off the hash tag
message = message.substring("#IMPORTANT".length);
gadgetContent += '<td class="priority important"></td><td class="message">';
gadgetContent += '<img src="' + imagesPrefixURL + '/images/Alert-ToggleOn.png" title="This is an Important Announcement." style="width: 18px; height: 18px; float: left;"/>';
gadgetContent += '<div style="margin-left: 25px;">' + message + '</div></td>';
} else {
gadgetContent += '<td class="priority"></td><td class="message">' + message + '</td>';
}
}
if (item.files) {
jQuery.each(item.files, function(index, item) {
gadgetContent += '<td class="priority"></td><td class="message"><i>Downloading attachments and images are not supported at this time.</i></td>'; // Future enhancement to support downloads
});
}
gadgetContent += '<td class="created" valign="top">' + created + '</td>';
gadgetContent += '<td class="person" valign="top"><div>';
//Make the avatar of the person show up in a circle of 20px
gadgetContent += '<div style="width: 20px; height: 20px; border-radius: 50%; background: url(' + personImage + '); background-size: 20px 20px; float:left;"></div>';
gadgetContent += '<div>&nbsp;&nbsp;&nbsp;&nbsp;by ' + personName + '</div></div></td></tr>';
//Leave a 6px gap to make each Annoucement Card look prominent
gadgetContent += '<tr style="height: 6px" class="autoRow"><td style="height: 6px; padding-top: 6px"></td></tr>';
$('#announce tbody:first-child').prepend(gadgetContent);
// Adjust the height of the gadget
gadgets.window.adjustHeight();
},
/**
* Determine whether the gadget needs to be updated.
* - There are new messages in the room
* - There was a message with a blue highlight and it is after ~ 1 min
* - There was a request to force an update (e.g. person details are available, access token wasn't available)
* If the above criterias are not met, do not refresh the gadget.
*/
handleMessageData = function (data) {
var contentChanged = false;
var currTime = new Date().getTime();
var timeSinceLastPost = (currTime - gadgetParams.lastMsgTimestamp) / 1000;
//Need this extra check to remove the blue highlighting on the announcement card (after a ~ min) even if there are no new announcements
if ((timeSinceLastPost > 60) && (timeSinceLastPost < 75)) contentChanged = true;
//Check if there are new messages
if (contentChanged === false) {
jQuery.each(data.items, function(index, item) {
//FOR EACH Message - check if item.created is > gadgetParams.lastMsgTimestamp
if (Date.parse(item.created) > gadgetParams.lastMsgTimestamp) {
contentChanged = true;
gadgetParams.lastMsgTimestamp = Date.parse(item.created);
}
});
}
if (forceGadgetUpdate || (contentChanged === true)) {
$('#announce .autoRow').remove();
var monthNames = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
jQuery.each(data.items, function(index, item) {
handleRepaintGadget(item, _getRoomMember(item.personId), currTime, monthNames);
});
forceGadgetUpdate = false;
}
},
/**
* Get the messages (Announcements) using the messages API. The number of
* messages retrieved is determined by the maxMessages parameter. Then,
* update the gadget with these messages.
*/
_updateGadget = function () {
clientLogs.log("_updateGadget(): Spark Room Id: " + gadgetParams.sparkRoom);
if (_getAccessToken()) {
// Get the 'Messages' from the Spark Room (from the roomId)
$.ajax({
url: 'https://api.ciscospark.com/v1/messages',
type: "get",
headers: { 'Authorization': 'Bearer ' + gadgetParams.token },
data: { 'roomId': gadgetParams.sparkRoom, 'max': gadgetParams.maxMessages },
success: function(data) {
$('#gadgetNotification').hide();
handleMessageData(data);
},
error: function(data) {
clientLogs.log("_updateGadget(): An error occurred while retrieving the messages for the room.");
$('#gadgetNotification').html("<font color=\"red\">An error occurred while retrieving the messages for the room.</font></div>");
$('#gadgetNotification').show();
}
});
} else {
forceGadgetUpdate = true;
clientLogs.log("_updateGadget(): The access token is not available yet. Try again in the next iteration.");
}
},
/**
* Post messages (Announcements) to the room. Update the gadget after posting.
*/
_postData = function () {
var className = $('#msg').attr('class');
var msgData = $('#msg').val();
if (className.indexOf("myinputimp") !== -1) msgData = "#IMPORTANT " + msgData;
$.ajax({
url: 'https://api.ciscospark.com/v1/messages',
type: "post",
headers: { 'Authorization': 'Bearer ' + gadgetParams.token, 'Content-Type': 'application/json' },
data: JSON.stringify({ "roomId": gadgetParams.sparkRoom, "text": msgData}),
success: function(data) {
clientLogs.log("Message Created: " + data.id);
$('#msg').val("");
if (className.indexOf("myinputimp") !== -1) {
$('#msg').removeClass("myinputimp");
$('#msg').addClass("myinputnotimp");
}
_updateGadget();
},
error: function(data) {
clientLogs.log("_postData(): An error occurred while sending the messages to the room.");
$('#gadgetNotification').html("<font color=\"red\">An error occurred while sending the messages to the room.</font></div>");
$('#gadgetNotification').show();
}
});
},
/**
* Toggles the Background URL to Important and Normal.
*/
_toggleImportant = function() {
var inputPlaceholder = "Type your announcement here and hit enter to send.";
var inputImpAddPlaceholder = "Your announcement will be marked as important.";
var className = $('#msg').attr('class');
var origImgSrc = $('#impIcon').prop('src');
if (className.indexOf("myinputnotimp") !== -1) {
$('#msg').removeClass("myinputnotimp");
$('#msg').addClass("myinputimp");
$('#msg').attr("placeholder", inputPlaceholder + " " + inputImpAddPlaceholder);
$('#impIcon').attr("src", imagesPrefixURL + "/images/Alert-ToggleOn-02.png");
} else {
$('#msg').removeClass("myinputimp");
$('#msg').addClass("myinputnotimp");
$('#msg').attr("placeholder", inputPlaceholder);
$('#impIcon').attr("src", imagesPrefixURL + "/images/Alert-ToggleOff-02.png");
}
},
/**
* Delete messages (Announcements). This is currently not used in the UI.
* Be aware of the Spark rules on who can delete whose messages. Rules vary based on locked / unlocked rooms.
*/
_deleteMessage = function (msgId) {
$.ajax({
url: 'https://api.ciscospark.com/v1/messages/' + msgId,
type: "delete",
headers: { 'Authorization': 'Bearer ' + gadgetParams.token },
success: function(data) {
clientLogs.log("Message Deleted: " + msgId);
}
});
},
/**
* Call the people details API for the given personId.
* Store the response in a map that will be used for the
* messages. When the response from this API comes back
* force an update to the gadget so that this information
* can be updated on the gadget.
*/
_getRoomMember = function (personId) {
var member = roomMembershipMap[personId];
if (member) {
return member;
} else {
// Call the Person Details API to get the 'Display Name' and 'Avatar' of the person who posted the 'Message'
$.ajax({
url: 'https://api.ciscospark.com/v1/people/' + personId,
type: "get",
headers: { 'Authorization': 'Bearer ' + gadgetParams.token },
success: function(personData) {
roomMembershipMap[personId] = personData;
// The Person Details are now available, so flag it
// to update the UI again.
forceGadgetUpdate = true;
},
error: function(personData) {
clientLogs.log("_getRoomMember(): An error occurred while retrieving the person details for " + personId);
}
});
}
},
/**
* Get the name of the room from the given roomId. Update the gadget's title
* with this room name.
*/
_getRoomName = function () {
clientLogs.log("_getRoomName(): Spark Room Id: " + gadgetParams.sparkRoom);
// Try to get the room name a max of 5 times
var done = false;
for(var i = 0; i < 5; i++) {
if(_getAccessToken()) {
// Get the 'Title' of the Spark Room (from the roomId given while configuring the finesse gadget)
$.ajax({
url: 'https://api.ciscospark.com/v1/rooms/' + gadgetParams.sparkRoom,
type: "get",
headers: { 'Authorization': 'Bearer ' + gadgetParams.token },
success: function(data) {
gadgets.window.setTitle(gadgetParams.gadgetTitle + " - " + data.title);
done = true;
},
error: function(data) {
// Log the error and let it loop back around
clientLogs.log("_getRoomName(): An error occurred while retrieving the room name for room id: " + gadgetParams.sparkRoom);
}
});
} else {
// Wait a second before trying again
clientLogs.log("_getRoomName(): The access token is not available yet. Try again in the next iteration.");
setTimeout(function(){}, 1000);
}
// Break out of the loop if the room name was found
if(done) break;
}
},
/**
* Get the access token from the cookie that was set by the
* OAuth. Return true if a token is found. False otherwise.
*/
_getAccessToken = function () {
clientLogs.log("_getAccessToken(): Getting the access token");
// Get the access token from the cookie that was set by the oauth
var value = "; " + document.cookie;
var parts = value.split("; " + "spark_access_token" + "=");
if (parts.length == 2) gadgetParams.token = parts.pop().split(";").shift();
// Return true if found. False otherwise.
if(gadgetParams.token) {
clientLogs.log("_getAccessToken(): Successfully retrieved the access token");
return true;
} else {
clientLogs.log("_getAccessToken(): Unable to retrieve the access token");
return false;
}
},
/**
* Handler for the onLoad of a User object. This occurs when the User object is initially read
* from the Finesse server. Any once only initialization should be done within this function.
*/
_handleUserLoad = function (user) { },
/**
* Handler for all User updates
*/
_handleUserChange = function(user) {
clientLogs.log("_handleUserChange() - " + user.getState());
// Remove the Spark Cookie of the User when the agent logs out.
// Right now there is no mechanism to log out the Spark user unless all the cookies are cleared from the browswer
if (user.getState() == "LOGOUT") document.cookie = 'spark_access_token=;domain=.' + finesseDomain + ';path=/;expires=Thu, 01 Jan 1970 00:00:01 GMT;';
},
/*
* The following runs when the Tab becomes active.
* Update the gadget and adjust the height of the gadget
*/
_handleActiveTab = function () {
clientLogs.log("_handleActiveTab(): The tab with the Spark team announcements gadget is now active.");
_updateGadget();
// Adjust the height of the gadget
gadgets.window.adjustHeight();
},
/**
* Timer tick callback handler.
* @param data
*/
_timerTickHandler = function (timerTickEvent) {
var start, end, diff, discardThreshold, processed;
start = (new Date()).getTime();
// Update the gadget if it is the first time, an update has been requested, or is at the _forceTickProcessingEvery interval
if ((_lastProcessedTimerTick === null) || forceGadgetUpdate || ((_lastProcessedTimerTick + _forceTickProcessingEvery) <= start)) {
_updateGadget();
_lastProcessedTimerTick = start;
}
end = (new Date()).getTime();
diff = end - start;
if (diff > _maxTimerCallbackThreshold) {
_clientLogs.log("Spark Team Announcement Gadget took too long to process timer tick (_maxTimerCallbackThreshold exceeded).");
}
};
/** @scope finesse.modules.SparkTeamAnnouncementsGadget */
return {
/**
* Performs all initialization for this gadget
*/
init : function () {
var prefs = new gadgets.Prefs(),
id = prefs.getString("id");
var cfg = finesse.gadget.Config;
clientLogs.init(gadgets.Hub, "SparkTeamAnnouncementsGadget", finesse.gadget.Config); //this gadget id will be logged as a part of the message
_initializeGadgetURLParams();
clientLogs.log("init() - gadgetParams.gadgetTitle: " + gadgetParams.gadgetTitle);
clientLogs.log("init() - gadgetParams.sparkRoom: " + gadgetParams.sparkRoom);
// Set the title to the name from the desktop layout parameter
gadgets.window.setTitle(gadgetParams.gadgetTitle);
// Initiate the ClientServices and the logger and then load the user object. ClientServices are
// initialized with a reference to the current configuration.
finesse.clientservices.ClientServices.init(finesse.gadget.Config);
user = new finesse.restservices.User({
id: id,
onLoad : _handleUserLoad,
onChange : _handleUserChange
});
containerServices = finesse.containerservices.ContainerServices.init();
clientLogs.log("Adding Tab Visible Handler...");
containerServices.addHandler(finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, _handleActiveTab);
clientLogs.log("Adding a timer handler...");
finesse.containerservices.ContainerServices.addHandler(finesse.containerservices.ContainerServices.Topics.TIMER_TICK_EVENT, _timerTickHandler);
// Check if there is already an access token store
// If so, load the gadget. If not, launch the OAuth
if(_getAccessToken()) {
_getRoomName();
_updateGadget();
} else {
// Get the access token by launching the Spark OAuth
var win = window.open(OAuthAuthorizationURL + "&service=spark");
win.focus();
}
// This requests to be notified of the currently active tab (in case we are already on it).
finesse.containerservices.ContainerServices.makeActiveTabReq();
}
};
}(jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment