First cut of online presence status for spaces

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7283 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mike Hatfield
2007-11-02 16:29:25 +00:00
parent b23aeb48c8
commit 184f784415
15 changed files with 475 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Presence Custom View Configuration</shortname>
<description>Add webscript aspect to space node</description>
<url>/ui/presence/config?n={nodeRef}&amp;w={webscriptUrl}</url>
<format default="html"/>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Online Presence Space Configurator</title>
<style>
body {
font-family: Verdana, Helvetica, sans-serif;
font-size: 10pt;
}
.label {
float: left;
width: 10em;
}
.data {
float: left;
}
.field {
clear: left;
float: left;
padding: 8px;
}
.action {
color: green;
font-weight: bold;
float: left;
clear: both;
}
</style>
</head>
<body>
<h3>Online Presence Space Configurator</h3>
<form action="${url.serviceContext}${url.match}" method="get">
<div class="field">
<span class="label">Space nodeRef:</span>
<span class="data"><input type="text" name="n" size="64" /><br />e.g. &quot;workspace://SpacesStore/e3741425-35cf-11dc-9762-4b73d0280543&quot;</span>
</div>
<div class="field">
<span class="label">Webscript URL:</span>
<span class="data"><input type="text" name="w" size="64" value="/wcs/ui/presence/status?nodeRef={noderef}" /></span>
</div>
<div class="field">
<span><input type="submit" value="submit" /></span>
</div>
</form>
<#if args.n?exists>
<div class="action">Aspect added to space.</div>
</#if>
</body>
</html>

View File

@@ -0,0 +1,7 @@
if (args["n"] != null)
{
var dest = search.findNode(args["n"]);
dest.addAspect("cm:webscriptable");
dest.properties["cm:webscript"] = args["w"];
dest.save();
}

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Presence Custom View</shortname>
<description>Show online presence status of space users</description>
<url>/ui/presence/status?nodeRef={noderef}</url>
<format default="html"/>
<authentication>user</authentication>
<transaction>required</transaction>
</webscript>

View File

@@ -0,0 +1,70 @@
<script type="text/javascript" src="${url.context}/scripts/ajax/presence.js"></script>
<div class="presenceTitle">Invited Users Presence Status</div>
<div class="presenceKey">
<img src="${url.context}/images/icons/presence_online.gif" width="16" height="16" alt="online" title="User is online">=online<br>
<img src="${url.context}/images/icons/presence_offline.gif" width="16" height="16" alt="offline" title="User is offline">=offline<br>
<img src="${url.context}/images/icons/presence_unknown.gif" width="16" height="16" alt="unknown" title="User status is unknown">=unknown
</div>
<div id="presenceContainer">
<#list presenceResults as pr>
<div class="presenceUser">
<div class="presenceStatus" rel="${pr[1]}" title=""></div>
<div class="presenceUsername">${pr[0]}</div>
</div>
</#list>
</div>
<style>
.presenceTitle
{
font-family: "Trebuchet MS", Verdana, Helvetica, sans-serif;
font-size: medium;
font-weight: bold;
margin: -8px 0px 4px;
float: left;
}
.presenceKey
{
float: right;
}
.presenceUser
{
float: left;
padding: 2px;
}
#presenceContainer
{
clear: left;
}
.presenceStatus
{
background-image: url(${url.context}/images/icons/ajax_anim.gif);
height: 16px;
width: 16px;
float: left;
margin: 0px 4px;
}
.presenceStatus.online
{
background-image: url(${url.context}/images/icons/presence_online.gif) !important;
}
.presenceStatus.offline
{
background-image: url(${url.context}/images/icons/presence_offline.gif) !important;
}
.presenceStatus.unknown
{
background-image: url(${url.context}/images/icons/presence_unknown.gif) !important;
}
.presenceUsername
{
float: left;
margin: 4px 0px 0px;
}
</style>

View File

@@ -0,0 +1,53 @@
main();
function main()
{
var nodeRef = args["nodeRef"];
var space = search.findNode(nodeRef);
var tokens, user, group;
var results = new Array();
for each(perm in space.permissions)
{
tokens = perm.split(";");
if (tokens[0] == "ALLOWED")
{
if (("CollaboratorContributorCoordinatorEditor").indexOf(tokens[2]) != -1)
{
user = people.getPerson(tokens[1]);
if (user != null)
{
pushUnique(results, user, presence.getDetails(user));
}
else
{
group = people.getGroup(tokens[1]);
if (group != null)
{
for each(user in people.getMembers(group))
{
pushUnique(results, user, presence.getDetails(user));
}
}
}
}
}
}
model.space = space;
model.presenceResults = results;
}
function pushUnique(results, user, details)
{
var fullName = user.properties["firstName"] + " " + user.properties["lastName"];
for (i=0; i < results.length; i++)
{
if (results[i][0] == fullName)
{
return;
}
}
results.push(new Array(fullName, details));
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing
*/
package org.alfresco.web.bean.ajax;
import java.io.IOException;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.web.app.servlet.ajax.InvokeCommand;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Bean which proxies requests to online presence servers.
*
* @author Mike Hatfield
*/
public class PresenceProxyBean
{
private static Log logger = LogFactory.getLog(PresenceProxyBean.class);
@InvokeCommand.ResponseMimetype(value=MimetypeMap.MIMETYPE_HTML)
public void proxyRequest() throws Exception
{
FacesContext fc = FacesContext.getCurrentInstance();
ResponseWriter out = fc.getResponseWriter();
Map<String, String> requestMap = fc.getExternalContext().getRequestParameterMap();
String url = (String)requestMap.get("url");
if (logger.isDebugEnabled())
logger.debug("PresenceProxyBean.proxyRequest() url=" + url);
if (url != null)
{
String response = getUrlResponse(url);
out.write(response);
}
}
/**
* Perform request
*
* @throws IOException
*/
public String getUrlResponse(String requestUrl)
{
String response = "";
HttpClient client = new HttpClient();
HttpMethod method = new GetMethod(requestUrl);
method.setRequestHeader("Accept", "*/*");
client.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
try
{
int statusCode = client.executeMethod(method);
if (statusCode == HttpStatus.SC_OK)
{
response = method.getResponseBodyAsString();
}
else
{
response = method.getStatusText();
}
}
catch (HttpException e)
{
response = e.getMessage();
}
catch (IOException e)
{
response = e.getMessage();
}
finally
{
// Release the connection.
method.releaseConnection();
}
return response;
}
}

View File

@@ -75,6 +75,8 @@ public class CreateUserWizard extends BaseWizardBean
protected String companyId = null;
protected String homeSpaceName = "";
protected NodeRef homeSpaceLocation = null;
protected String presenceProvider = null;
protected String presenceUsername = null;
/** AuthenticationService bean reference */
private AuthenticationService authenticationService;
@@ -144,6 +146,8 @@ public class CreateUserWizard extends BaseWizardBean
this.companyId = "";
this.homeSpaceName = "";
this.homeSpaceLocation = getDefaultHomeSpace();
this.presenceProvider = "";
this.presenceUsername = "";
}
/**
@@ -187,6 +191,40 @@ public class CreateUserWizard extends BaseWizardBean
this.companyId = companyId;
}
/**
* @return Returns the presenceProvider.
*/
public String getPresenceProvider()
{
return this.presenceProvider;
}
/**
* @param presenceProvider
* The presenceProvider to set.
*/
public void setPresenceProvider(String presenceProvider)
{
this.presenceProvider = presenceProvider;
}
/**
* @return Returns the presenceUsername.
*/
public String getPresenceUsername()
{
return this.presenceUsername;
}
/**
* @param presenceUsername
* The presenceUsername to set.
*/
public void setPresenceUsername(String presenceUsername)
{
this.presenceUsername = presenceUsername;
}
/**
* @return Returns the email.
*/

View File

@@ -70,6 +70,8 @@ public class EditUserWizard extends CreateUserWizard
this.userName = (String) props.get("userName");
this.email = (String) props.get("email");
this.companyId = (String) props.get("organizationId");
this.presenceProvider = (String) props.get("presenceProvider");
this.presenceUsername = (String) props.get("presenceUsername");
// calculate home space name and parent space Id from homeFolderId
this.homeSpaceLocation = null; // default to Company root space
@@ -177,6 +179,8 @@ public class EditUserWizard extends CreateUserWizard
props.put(ContentModel.PROP_HOMEFOLDER, newHomeFolderRef);
props.put(ContentModel.PROP_EMAIL, this.email);
props.put(ContentModel.PROP_ORGID, this.companyId);
props.put(ContentModel.PROP_PRESENCEPROVIDER, this.presenceProvider);
props.put(ContentModel.PROP_PRESENCEUSERNAME, this.presenceUsername);
this.nodeService.setProperties(nodeRef, props);
// TODO: RESET HomeSpace Ref found in top-level navigation bar!

View File

@@ -4044,6 +4044,15 @@
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<description>
Bean proxying ajax calls for the online Presence webscript
</description>
<managed-bean-name>PresenceProxyBean</managed-bean-name>
<managed-bean-class>org.alfresco.web.bean.ajax.PresenceProxyBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<description>
Bean that returns manages the tree data for the navigator component

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

View File

@@ -86,4 +86,22 @@
</f:verbatim><h:inputText value="#{WizardManager.bean.companyId}" size="35" maxlength="1024" /><f:verbatim>
</td>
</tr>
<tr>
<td></f:verbatim><h:outputText value="Presence Provider"/><f:verbatim>:</td>
<td>
</f:verbatim><h:selectOneMenu value="#{WizardManager.bean.presenceProvider}">
<f:selectItem itemValue="" itemLabel="(None)"/>
<f:selectItem itemValue="skype" itemLabel="Skype"/>
<f:selectItem itemValue="yahoo" itemLabel="Yahoo"/>
</h:selectOneMenu><f:verbatim>
</td>
</tr>
<tr>
<td></f:verbatim><h:outputText value="Presence Username"/><f:verbatim>:</td>
<td>
</f:verbatim><h:inputText value="#{WizardManager.bean.presenceUsername}" size="35" maxlength="256" /><f:verbatim>
</td>
</tr>
</table></f:verbatim>

View File

@@ -0,0 +1,93 @@
/*
* Prerequisites: mootools.v1.11.js
*/
var Presence =
{
init: function()
{
window.contextPath = Presence.getContextPath();
var users = $$("#presenceContainer .presenceStatus");
users.each(function(user, i)
{
// ajax call to load online status
var userDetails = user.attributes["rel"].value.split("|");
var proxyURL = window.contextPath + "/ajax/invoke/PresenceProxyBean.proxyRequest";
var statusURL = Presence.getStatusURL(userDetails);
if (statusURL != "")
{
var myAjax = new Ajax(proxyURL, {
method: 'get',
headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'},
onComplete: function(textResponse, xmlResponse)
{
var statusType = Presence.getStatusType(userDetails[0], textResponse);
user.addClass(statusType);
}
});
myAjax.request("url=" + escape(statusURL));
}
else
{
user.addClass("unknown");
}
});
},
/* Calculates and returns the context path for the current page */
getContextPath: function()
{
var path = window.location.pathname;
var idx = path.indexOf("/", 1);
var contextPath = "";
if (idx != -1)
{
contextPath = path.substring(0, idx);
}
else
{
contextPath = "";
}
return contextPath;
},
getStatusURL: function(userDetails)
{
var provider = userDetails[0];
var username = userDetails[1];
var statusURL = "";
switch(provider)
{
case "skype":
statusURL = "http://mystatus.skype.com/" + username + ".txt";
break;
case "yahoo":
statusURL = "http://opi.yahoo.com/online?u=" + username + "&m=t&t=1";
break;
}
return statusURL;
},
getStatusType: function(provider, response)
{
var statusType = "unknown";
switch(provider)
{
case "skype":
statusType = (response == "Online") ? "online" : "offline";
break;
case "yahoo":
statusType = (response == "01") ? "online" : "offline";
break;
}
return statusType;
}
}
window.addEvent('domready', Presence.init);