diff --git a/config/alfresco/templates/activities/org/alfresco/site/user-joined.atomentry.ftl b/config/alfresco/templates/activities/org/alfresco/site/user-joined.atomentry.ftl
new file mode 100644
index 0000000000..3d3eb53e3f
--- /dev/null
+++ b/config/alfresco/templates/activities/org/alfresco/site/user-joined.atomentry.ftl
@@ -0,0 +1,11 @@
+
+ ${memberFirstName}<#if memberLastName??> ${memberLastName}#if> joined ${siteNetwork} site
+
+
+ ${id}
+ ${xmldate(date)}
+ ${memberFirstName}<#if memberLastName??> ${memberLastName}#if> joined ${siteNetwork} site (with role ${role})
+
+ ${userId}
+
+
\ No newline at end of file
diff --git a/config/alfresco/templates/activities/org/alfresco/site/user-left.atomentry.ftl b/config/alfresco/templates/activities/org/alfresco/site/user-left.atomentry.ftl
new file mode 100644
index 0000000000..37450c83d2
--- /dev/null
+++ b/config/alfresco/templates/activities/org/alfresco/site/user-left.atomentry.ftl
@@ -0,0 +1,11 @@
+
+ ${memberFirstName}<#if memberLastName??> ${memberLastName}#if> left ${siteNetwork} site
+
+
+ ${id}
+ ${xmldate(date)}
+ ${memberFirstName}<#if memberLastName??> ${memberLastName}#if> left ${siteNetwork} site
+
+ ${userId}
+
+
\ No newline at end of file
diff --git a/config/alfresco/templates/activities/org/alfresco/site/user-role-changed.atomentry.ftl b/config/alfresco/templates/activities/org/alfresco/site/user-role-changed.atomentry.ftl
new file mode 100644
index 0000000000..8c806fec20
--- /dev/null
+++ b/config/alfresco/templates/activities/org/alfresco/site/user-role-changed.atomentry.ftl
@@ -0,0 +1,11 @@
+
+ ${memberFirstName}<#if memberLastName??> ${memberLastName}#if> role changed for ${siteNetwork} site
+
+
+ ${id}
+ ${xmldate(date)}
+ ${memberFirstName}<#if memberLastName??> ${memberLastName}#if> role changed to ${role} for ${siteNetwork} site
+
+ ${userId}
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/web/scripts/activities/SiteActivitySystemTest.java b/source/java/org/alfresco/repo/web/scripts/activities/SiteActivitySystemTest.java
new file mode 100644
index 0000000000..8fb9048467
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/activities/SiteActivitySystemTest.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2005-2008 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.repo.web.scripts.activities;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.alfresco.repo.site.SiteModel;
+import org.alfresco.util.Base64;
+import org.alfresco.util.ISO8601DateFormat;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Simple Activity Service system test (requires remote repo to be running) using site (membership) activities
+ *
+ * @author janv
+ */
+public class SiteActivitySystemTest extends TestCase
+{
+ private static Log logger = LogFactory.getLog(SiteActivitySystemTest.class);
+
+ // TODO - use test property file
+ private static final String REPO = "http://localhost:8080/alfresco";
+
+ // web service (SOAP) - temporary (see below)
+ private static final String WEBSERVICE_ENDPOINT = REPO + "/api";
+
+ private static final String URL_AUTH = "/AuthenticationService";
+ private static final String URL_ADMIN = "/AdministrationService";
+
+ // web script (REST)
+ private static final String WEBSCRIPT_ENDPOINT = REPO + "/service";
+
+ // Site Service part-URLs
+ private static final String URL_SITES = "/api/sites";
+ private static final String URL_MEMBERSHIPS = "/memberships";
+
+ // Activity Service part-URLs
+ private static final String URL_ACTIVITIES = "/api/activities";
+ private static final String URL_SITE_FEED = "/feed/site";
+ private static final String URL_USER_FEED = "/feed/user";
+
+
+ private static boolean setup = false;
+
+ private static String shortName = null;
+ private static String user1 = null;
+ private static String user2 = null;
+ private static String user3 = null;
+
+
+ public SiteActivitySystemTest()
+ {
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ if (! setup)
+ {
+ String testid = ""+System.currentTimeMillis();
+
+ shortName = "testSite_" + testid;
+ user1 = "testSite_user1_" + testid;
+ user2 = "testSite_user2_" + testid;
+ user3 = "testSite_user3_" + testid;
+
+ // pre-create users
+
+ createUser(user1, "password");
+ createUser(user2, "password");
+ createUser(user3, "password");
+
+ setup = true;
+ }
+
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testCreateSite() throws Exception
+ {
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ JSONObject site = new JSONObject();
+ site.put("sitePreset", "myPreset");
+ site.put("shortName", shortName);
+ site.put("title", "myTitle");
+ site.put("description", "myDescription");
+ site.put("isPublic", true);
+
+ String url = WEBSCRIPT_ENDPOINT + URL_SITES;
+ String response = callPostWebScript(url, ticket, site.toString());
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testCreateSite");
+ logger.debug("--------------");
+ logger.debug(url);
+ logger.debug(response);
+ }
+ }
+
+ public void testGetSite() throws Exception
+ {
+ // relies on testCreateSite
+
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ String url = WEBSCRIPT_ENDPOINT + URL_SITES + "/" + shortName;
+ String response = callGetWebScript(url, ticket);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testGetSite");
+ logger.debug("-----------");
+ logger.debug(url);
+ logger.debug(response);
+ }
+ }
+
+ public void testGetSiteFeed1() throws Exception
+ {
+ // relies on testCreateSite
+
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ String url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_SITE_FEED + "/" + shortName + "?format=json";
+ String jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(0, ja.length());
+ }
+ else
+ {
+ fail("Error getting site feed");
+ }
+ }
+
+ public void testGetUserFeeds1_asAdmin() throws Exception
+ {
+ // relies on testCreateSite
+
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ String url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_USER_FEED + "/" + user1 + "?format=json";
+ String jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(0, ja.length());
+ }
+ else
+ {
+ fail("Error getting user feed");
+ }
+
+ url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_USER_FEED + "/" + user2 + "?format=json";
+ jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(0, ja.length());
+ }
+ else
+ {
+ fail("Error getting user feed");
+ }
+
+ url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_USER_FEED + "/" + user3 + "?format=json";
+ jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(0, ja.length());
+ }
+ else
+ {
+ fail("Error getting user feed");
+ }
+ }
+
+ public void testMemberships() throws Exception
+ {
+ // relies on testCreateSite
+
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ // add member -> join site
+ testAddMembership(user1, ticket, SiteModel.SITE_CONSUMER);
+ testAddMembership(user2, ticket, SiteModel.SITE_MANAGER);
+ testAddMembership(user3, ticket, SiteModel.SITE_COLLABORATOR);
+
+ // update member -> change role
+ testUpdateMembership(user1, ticket, SiteModel.SITE_MANAGER);
+ testUpdateMembership(user2, ticket, SiteModel.SITE_COLLABORATOR);
+ testUpdateMembership(user3, ticket, SiteModel.SITE_CONSUMER);
+
+ // add pause - otherwise, activity service will not generate feed entries (since they will have already left the site)
+ Thread.sleep(90000); // 1 min
+
+ // remove member -> leave site
+ testRemoveMembership(user1, ticket);
+ testRemoveMembership(user2, ticket);
+ testRemoveMembership(user3, ticket);
+
+ // add pause
+ Thread.sleep(60000); // 1 min
+ }
+
+ public void testGetSiteFeed2() throws Exception
+ {
+ // relies on testCreateSite, testMemberships
+
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ String url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_SITE_FEED + "/" + shortName + "?format=json";
+ String jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testGetSiteFeed2");
+ logger.debug("----------------");
+ logger.debug(url);
+ logger.debug(jsonArrayResult);
+ }
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(9, ja.length());
+ }
+ else
+ {
+ fail("Error getting site feed");
+ }
+ }
+
+ public void testGetUserFeeds2_asAdmin() throws Exception
+ {
+ // relies on testCreateSite, testMemberships
+
+ String ticket = callLoginWebScript(WEBSCRIPT_ENDPOINT, "admin", "admin");
+
+ String url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_USER_FEED + "/" + user1 + "?format=json";
+ String jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testGetUserFeeds2_asAdmin: user1");
+ logger.debug("--------------------------");
+ logger.debug(url);
+ logger.debug(jsonArrayResult);
+ }
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(6, ja.length());
+ }
+ else
+ {
+ fail("Error getting user feed");
+ }
+
+ url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_USER_FEED + "/" + user2 + "?format=json";
+ jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testGetUserFeeds2_asAdmin: user2");
+ logger.debug("--------------------------");
+ logger.debug(url);
+ logger.debug(jsonArrayResult);
+ }
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(6, ja.length());
+ }
+ else
+ {
+ fail("Error getting user feed");
+ }
+
+ url = WEBSCRIPT_ENDPOINT + URL_ACTIVITIES + URL_USER_FEED + "/" + user3 + "?format=json";
+ jsonArrayResult = callGetWebScript(url, ticket);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testGetUserFeeds2_asAdmin: user3");
+ logger.debug("--------------------------");
+ logger.debug(url);
+ logger.debug(jsonArrayResult);
+ }
+
+ if (jsonArrayResult != null)
+ {
+ JSONArray ja = new JSONArray(jsonArrayResult);
+ assertEquals(6, ja.length());
+ }
+ else
+ {
+ fail("Error getting user feed");
+ }
+ }
+
+ private void testAddMembership(String userName, String ticket, String role) throws Exception
+ {
+ // Build the JSON membership object
+ JSONObject membership = new JSONObject();
+ membership.put("role", role);
+ JSONObject person = new JSONObject();
+ person.put("userName", userName);
+ membership.put("person", person);
+
+ String url = WEBSCRIPT_ENDPOINT + URL_SITES + "/" + shortName + URL_MEMBERSHIPS;
+ String response = callPostWebScript(url, ticket, membership.toString());
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testAddMembership: " + userName);
+ logger.debug("------------------");
+ logger.debug(url);
+ logger.debug(response);
+ }
+ }
+
+ private void testUpdateMembership(String userName, String ticket, String role) throws Exception
+ {
+ // Build the JSON membership object
+ JSONObject membership = new JSONObject();
+ membership.put("role", role);
+ JSONObject person = new JSONObject();
+ person.put("userName", userName);
+ membership.put("person", person);
+
+ String url = WEBSCRIPT_ENDPOINT + URL_SITES + "/" + shortName + URL_MEMBERSHIPS + "/" + userName;
+ String response = callPutWebScript(url, ticket, membership.toString());
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testUpdateMembership: " + userName);
+ logger.debug("------------------");
+ logger.debug(url);
+ logger.debug(response);
+ }
+ }
+
+ private void testRemoveMembership(String userName, String ticket) throws Exception
+ {
+ String url = WEBSCRIPT_ENDPOINT + URL_SITES + "/" + shortName + URL_MEMBERSHIPS + "/" + userName;
+ String response = callDeleteWebScript(url, ticket);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("testRemoveMembership: " + userName);
+ logger.debug("---------------------");
+ logger.debug(url);
+ logger.debug(response);
+ }
+ }
+
+ protected String callGetWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException
+ {
+ return callOutWebScript(urlString, "GET", ticket);
+ }
+
+ protected String callDeleteWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException
+ {
+ return callOutWebScript(urlString, "DELETE", ticket);
+ }
+
+ protected String callPostWebScript(String urlString, String ticket, String data) throws MalformedURLException, URISyntaxException, IOException
+ {
+ return callInOutWebScript(urlString, "POST", ticket, data);
+ }
+
+ protected String callPutWebScript(String urlString, String ticket, String data) throws MalformedURLException, URISyntaxException, IOException
+ {
+ return callInOutWebScript(urlString, "PUT", ticket, data);
+ }
+
+ private String callOutWebScript(String urlString, String method, String ticket) throws MalformedURLException, URISyntaxException, IOException
+ {
+ URL url = new URL(urlString);
+
+ HttpURLConnection conn = (HttpURLConnection)url.openConnection();
+ conn.setRequestMethod(method);
+
+ if (ticket != null)
+ {
+ // add Base64 encoded authorization header
+ // refer to: http://wiki.alfresco.com/wiki/Web_Scripts_Framework#HTTP_Basic_Authentication
+ conn.addRequestProperty("Authorization", "Basic " + Base64.encodeBytes(ticket.getBytes()));
+ }
+
+ String result = null;
+ InputStream is = null;
+ BufferedReader br = null;
+
+ try
+ {
+ is = conn.getInputStream();
+ br = new BufferedReader(new InputStreamReader(is));
+
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while(((line = br.readLine()) !=null)) {
+ sb.append(line);
+ }
+
+ result = sb.toString();
+ }
+ finally
+ {
+ if (br != null) { br.close(); };
+ if (is != null) { is.close(); };
+ }
+
+ return result;
+ }
+
+ private String callInOutWebScript(String urlString, String method, String ticket, String data) throws MalformedURLException, URISyntaxException, IOException
+ {
+ return callInOutWeb(urlString, method, ticket, data, "application/json", null);
+ }
+
+ private String callInOutWeb(String urlString, String method, String ticket, String data, String contentType, String soapAction) throws MalformedURLException, URISyntaxException, IOException
+ {
+ URL url = new URL(urlString);
+
+ HttpURLConnection conn = (HttpURLConnection)url.openConnection();
+ conn.setRequestMethod(method);
+
+ conn.setRequestProperty("Content-type", contentType);
+
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ conn.setUseCaches (false);
+
+ if (soapAction != null)
+ {
+ conn.setRequestProperty("SOAPAction", soapAction);
+ }
+
+ if (ticket != null)
+ {
+ // add Base64 encoded authorization header
+ // refer to: http://wiki.alfresco.com/wiki/Web_Scripts_Framework#HTTP_Basic_Authentication
+ conn.addRequestProperty("Authorization", "Basic " + Base64.encodeBytes(ticket.getBytes()));
+ }
+
+ String result = null;
+ BufferedReader br = null;
+ DataOutputStream wr = null;
+ OutputStream os = null;
+ InputStream is = null;
+
+ try
+ {
+ os = conn.getOutputStream();
+ wr = new DataOutputStream(os);
+ wr.write(data.getBytes());
+ wr.flush();
+ }
+ finally
+ {
+ if (wr != null) { wr.close(); };
+ if (os != null) { os.close(); };
+ }
+
+ try
+ {
+ is = conn.getInputStream();
+ br = new BufferedReader(new InputStreamReader(is));
+
+ String line = null;
+ StringBuffer sb = new StringBuffer();
+ while(((line = br.readLine()) !=null))
+ {
+ sb.append(line);
+ }
+
+ result = sb.toString();
+ }
+ finally
+ {
+ if (br != null) { br.close(); };
+ if (is != null) { is.close(); };
+ }
+
+ return result;
+ }
+
+ protected String callLoginWebScript(String serviceUrl, String username, String password) throws MalformedURLException, URISyntaxException, IOException
+ {
+ // Refer to: http://wiki.alfresco.com/wiki/Web_Scripts_Framework#HTTP_Basic_Authentication
+ String ticketResult = callGetWebScript(serviceUrl+"/api/login?u="+username+"&pw="+password, null);
+
+ if (ticketResult != null)
+ {
+ int startTag = ticketResult.indexOf("");
+ int endTag = ticketResult.indexOf("");
+ if ((startTag != -1) && (endTag != -1))
+ {
+ ticketResult = ticketResult.substring(startTag+("".length()), endTag);
+ }
+ }
+
+ return ticketResult;
+ }
+
+ // TODO - replace with Create Person REST API when it becomes available
+
+ protected void createUser(String username, String password) throws MalformedURLException, URISyntaxException, IOException
+ {
+ String ticket = webServiceStartSession("admin", "admin");
+ webServiceCreateUser(username, password, ticket);
+ webServiceEndSession(ticket);
+ }
+
+ private String webServiceStartSession(String username, String password) throws MalformedURLException, URISyntaxException, IOException
+ {
+ String soapCall =
+ ""+
+ ""+
+ ""+
+ ""+username+""+
+ ""+password+""+
+ "";
+
+ String response = callInOutWeb(WEBSERVICE_ENDPOINT + URL_AUTH, "POST", null, soapCall, "text/xml; charset=utf-8", "\"http://www.alfresco.org/ws/service/authentication/1.0/startSession\"");
+
+ String ticket = null;
+
+ if (response != null)
+ {
+ int idx1 = response.indexOf("");
+ if (idx1 != -1)
+ {
+ int idx2 = response.indexOf("");
+ if (idx2 != -1)
+ {
+ ticket = response.substring(idx1+"".length(), idx2);
+ }
+ }
+ }
+
+ return ticket;
+ }
+
+ private void webServiceCreateUser(String username, String password, String ticket) throws MalformedURLException, URISyntaxException, IOException
+ {
+ Date now = new Date();
+ String startTime = ISO8601DateFormat.format(now);
+ String expireTime = ISO8601DateFormat.format(new Date(now.getTime() + 5*60*1000));
+
+ String soapCall =
+ ""+
+ ""+
+ ""+
+ ""+
+ ""+startTime+""+expireTime+""+
+ "ticket"+ticket+""+
+ ""+
+ ""+username+""+password+""+
+ "{http://www.alfresco.org/model/content/1.0}homeFolder"+
+ "false"+
+ "workspace:////SpacesStore" +
+ "{http://www.alfresco.org/model/content/1.0}firstName"+
+ "false"+
+ ""+"FN_"+username+""+
+ "{http://www.alfresco.org/model/content/1.0}middleName"+
+ "false"+
+ ""+
+ "{http://www.alfresco.org/model/content/1.0}lastName"+
+ "false" +
+ ""+"LN_"+username+"" +
+ "{http://www.alfresco.org/model/content/1.0}email"+
+ "false"+
+ "email1210929178773"+
+ "{http://www.alfresco.org/model/content/1.0}organizationId"+
+ "false"+
+ "org1210929178773";
+
+ callInOutWeb(WEBSERVICE_ENDPOINT + URL_ADMIN, "POST", null, soapCall, "text/xml; charset=utf-8", "\"http://www.alfresco.org/ws/service/administration/1.0/createUsers\""); // ignore response
+ }
+
+ private void webServiceEndSession(String ticket) throws MalformedURLException, URISyntaxException, IOException
+ {
+ String soapCall =
+ ""+
+ ""+
+ ""+
+ ""+ticket+""+
+ "";
+
+ callInOutWeb(WEBSERVICE_ENDPOINT + URL_AUTH, "POST", null, soapCall, "text/xml; charset=utf-8", "\"http://www.alfresco.org/ws/service/authentication/1.0/endSession\""); // ignore response
+ }
+}