mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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:
@@ -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}&w={webscriptUrl}</url>
|
||||
<format default="html"/>
|
||||
<authentication>user</authentication>
|
||||
<transaction>required</transaction>
|
||||
</webscript>
|
@@ -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. "workspace://SpacesStore/e3741425-35cf-11dc-9762-4b73d0280543"</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>
|
@@ -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();
|
||||
}
|
@@ -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>
|
@@ -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>
|
@@ -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));
|
||||
}
|
113
source/java/org/alfresco/web/bean/ajax/PresenceProxyBean.java
Normal file
113
source/java/org/alfresco/web/bean/ajax/PresenceProxyBean.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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!
|
||||
|
@@ -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
|
||||
|
BIN
source/web/images/icons/presence_offline.gif
Normal file
BIN
source/web/images/icons/presence_offline.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 372 B |
BIN
source/web/images/icons/presence_online.gif
Normal file
BIN
source/web/images/icons/presence_online.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 591 B |
BIN
source/web/images/icons/presence_unknown.gif
Normal file
BIN
source/web/images/icons/presence_unknown.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 389 B |
@@ -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>
|
93
source/web/scripts/ajax/presence.js
Normal file
93
source/web/scripts/ajax/presence.js
Normal 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);
|
Reference in New Issue
Block a user