diff --git a/config/alfresco/activities/activities-SqlMapConfig.xml b/config/alfresco/activities/activities-SqlMapConfig.xml
new file mode 100644
index 0000000000..e3b29239fe
--- /dev/null
+++ b/config/alfresco/activities/activities-SqlMapConfig.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/activities/activities-feed-context.xml b/config/alfresco/activities/activities-feed-context.xml
new file mode 100644
index 0000000000..535b2e83d4
--- /dev/null
+++ b/config/alfresco/activities/activities-feed-context.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+ classpath:alfresco/activities/activities-SqlMapConfig.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 20160
+
+
+
+
+
+
+
+ 30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityFeed.xml b/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityFeed.xml
new file mode 100644
index 0000000000..040d7d0752
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityFeed.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into alf_activity_feed (activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date)
+ values (#activityType#, #activitySummary#, #activitySummaryFormat#, #feedUserId#, #postUserId#, #postDate#, #postId#, #siteNetwork#, #appTool#, #feedDate#)
+
+
+
+ CALL IDENTITY()
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityFeedControl.xml b/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityFeedControl.xml
new file mode 100644
index 0000000000..d1b553a72c
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityFeedControl.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into alf_activity_feed_control (feed_user_id, site_network, app_tool, last_modified)
+ values (#feedUserId#, #siteNetwork#, #appTool#, #lastModified#)
+
+
+
+ CALL IDENTITY()
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityPost.xml b/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityPost.xml
new file mode 100755
index 0000000000..2d479a6d32
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.HSQLDialect/ActivityPost.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into alf_activity_post (status, activity_data, post_user_id, post_date, activity_type, site_network, app_tool, job_task_node, last_modified)
+ values (#status#, #activityData#, #userId#, #postDate#, #activityType#, #siteNetwork#, #appTool#, #jobTaskNode#, #lastModified#)
+
+
+
+ CALL IDENTITY()
+
+
+
+
+
+
+
+
+
+ update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
+ where sequence_id = #id#
+ and status != #status#
+
+
+
+ update alf_activity_post set status = #status#, last_modified=#lastModified#
+ where sequence_id = #id#
+ and status != #status#
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityFeed.xml b/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityFeed.xml
new file mode 100644
index 0000000000..0e046c8840
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityFeed.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into alf_activity_feed (activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date)
+ values (#activityType#, #activitySummary#, #activitySummaryFormat#, #feedUserId#, #postUserId#, #postDate#, #postId#, #siteNetwork#, #appTool#, #feedDate#)
+
+
+
+ SELECT LAST_INSERT_ID() AS value
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityFeedControl.xml b/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityFeedControl.xml
new file mode 100644
index 0000000000..b0ac596003
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityFeedControl.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into alf_activity_feed_control (feed_user_id, site_network, app_tool, last_modified)
+ values (#feedUserId#, #siteNetwork#, #appTool#, #lastModified#)
+
+
+
+ SELECT LAST_INSERT_ID() AS value
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityPost.xml b/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityPost.xml
new file mode 100755
index 0000000000..1c080740e8
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.MySQLInnoDBDialect/ActivityPost.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into alf_activity_post (status, activity_data, post_user_id, post_date, activity_type, site_network, app_tool, job_task_node, last_modified)
+ values (#status#, #activityData#, #userId#, #postDate#, #activityType#, #siteNetwork#, #appTool#, #jobTaskNode#, #lastModified#)
+
+
+
+ SELECT LAST_INSERT_ID() AS value
+
+
+
+
+
+
+
+
+
+ update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
+ where sequence_id = #id#
+ and status != #status#
+
+
+
+ update alf_activity_post set status = #status#, last_modified=#lastModified#
+ where sequence_id = #id#
+ and status != #status#
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityFeed.xml b/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityFeed.xml
new file mode 100644
index 0000000000..176f84f708
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityFeed.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select alf_activity_feed_seq.nextval as value from dual
+
+
+ insert into alf_activity_feed (id, activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date)
+ values (#id#, #activityType#, #activitySummary#, #activitySummaryFormat#, #feedUserId#, #postUserId#, #postDate#, #postId#, #siteNetwork#, #appTool#, #feedDate#)
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityFeedControl.xml b/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityFeedControl.xml
new file mode 100644
index 0000000000..d8afdccf6b
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityFeedControl.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select alf_activity_feed_control_seq.nextval as value from dual
+
+
+ insert into alf_activity_feed_control (id, feed_user_id, site_network, app_tool, last_modified)
+ values (#id#, #feedUserId#, #siteNetwork#, #appTool#, #lastModified#)
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityPost.xml b/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityPost.xml
new file mode 100755
index 0000000000..b2f5dc0895
--- /dev/null
+++ b/config/alfresco/activities/org.hibernate.dialect.Oracle9Dialect/ActivityPost.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select alf_activity_post_seq.nextval as value from dual
+
+
+ insert into alf_activity_post (sequence_id, status, activity_data, post_user_id, post_date, activity_type, site_network, app_tool, job_task_node, last_modified)
+ values (#id#, #status#, #activityData#, #userId#, #postDate#, #activityType#, #siteNetwork#, #appTool#, #jobTaskNode#, #lastModified#)
+
+
+
+
+
+
+
+
+ update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
+ where sequence_id = #id#
+ and status != #status#
+
+
+
+ update alf_activity_post set status = #status#, last_modified=#lastModified#
+ where sequence_id = #id#
+ and status != #status#
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml
index 1668af9ed5..9a37fee7c6 100644
--- a/config/alfresco/application-context.xml
+++ b/config/alfresco/application-context.xml
@@ -12,7 +12,8 @@
-
+
+
diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index 0dc044d49e..9977930c08 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -43,6 +43,7 @@
classpath:alfresco/dbscripts/create/2.2/${db.script.dialect}/AlfrescoPostCreate-2.2-MappedFKIndexes.sql
classpath:alfresco/dbscripts/create/2.2/${db.script.dialect}/AlfrescoPostCreate-2.2-Extra.sql
+ classpath:alfresco/dbscripts/create/3.0/${db.script.dialect}/create-activities-tables.sql
@@ -59,6 +60,7 @@
+
diff --git a/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.HSQLDialect/create-activities-tables.sql b/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.HSQLDialect/create-activities-tables.sql
new file mode 100644
index 0000000000..8abb15689b
--- /dev/null
+++ b/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.HSQLDialect/create-activities-tables.sql
@@ -0,0 +1,59 @@
+--
+-- Title: Activities Schema
+-- Database: HSQL
+-- Since: V3.0.0 Schema
+--
+-- Note: The Activities schema is NOT managed by Hibernate
+--
+
+
+CREATE TABLE alf_activity_post (
+ sequence_id bigint generated by default as identity (start with 1),
+ post_date timestamp NOT NULL,
+ status varchar(10) NOT NULL,
+ activity_data varchar(4000) NOT NULL,
+ post_user_id varchar(255) NOT NULL,
+ job_task_node integer NOT NULL,
+ site_network varchar(255) default NULL,
+ app_tool varchar(36) default NULL,
+ activity_type varchar(255) NOT NULL,
+ last_modified timestamp NOT NULL,
+ primary key (sequence_id)
+);
+
+CREATE INDEX jobtasknode_idx on alf_activity_post(job_task_node);
+CREATE INDEX status_idx on alf_activity_post(status);
+
+
+CREATE TABLE alf_activity_feed (
+ id bigint generated by default as identity (start with 1),
+ post_id bigint default NULL,
+ post_date timestamp NOT NULL,
+ activity_summary varchar(4000) default NULL,
+ feed_user_id varchar(255) NOT NULL,
+ activity_type varchar(255) NOT NULL,
+ activity_format varchar(10) default NULL,
+ site_network varchar(255) default NULL,
+ app_tool varchar(36) default NULL,
+ post_user_id varchar(255) NOT NULL,
+ feed_date timestamp NOT NULL,
+ primary key (id)
+);
+
+CREATE INDEX postdate_idx ON alf_activity_feed(post_date);
+CREATE INDEX feeduserid_idx ON alf_activity_feed(feed_user_id);
+CREATE INDEX postuserid_idx ON alf_activity_feed(post_user_id);
+CREATE INDEX sitenetwork_idx ON alf_activity_feed(site_network);
+CREATE INDEX activityformat_idx ON alf_activity_feed(activity_format);
+
+
+CREATE TABLE alf_activity_feed_control (
+ id bigint generated by default as identity (start with 1),
+ feed_user_id varchar(255) NOT NULL,
+ site_network varchar(255) NOT NULL,
+ app_tool varchar(36) default NULL,
+ last_modified timestamp NOT NULL,
+ primary key (id)
+);
+
+CREATE INDEX feedcontroluserid_idx ON alf_activity_feed_control(feed_user_id);
diff --git a/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.MySQLInnoDBDialect/create-activities-tables.sql b/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.MySQLInnoDBDialect/create-activities-tables.sql
new file mode 100644
index 0000000000..8698e3ebb6
--- /dev/null
+++ b/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.MySQLInnoDBDialect/create-activities-tables.sql
@@ -0,0 +1,56 @@
+--
+-- Title: Activities Schema
+-- Database: MySQL
+-- Since: V3.0.0 Schema
+--
+-- Note: The Activities schema is NOT managed by Hibernate
+--
+
+
+CREATE TABLE alf_activity_post (
+ sequence_id bigint NOT NULL auto_increment,
+ post_date timestamp NOT NULL,
+ status varchar(10) NOT NULL,
+ activity_data varchar(4000) NOT NULL,
+ post_user_id varchar(255) NOT NULL,
+ job_task_node int(11) NOT NULL,
+ site_network varchar(255) default NULL,
+ app_tool varchar(36) default NULL,
+ activity_type varchar(255) NOT NULL,
+ last_modified timestamp NOT NULL,
+ PRIMARY KEY (sequence_id),
+ KEY jobtasknode_idx (job_task_node),
+ KEY status_idx (status)
+) type=InnoDB;
+
+
+CREATE TABLE alf_activity_feed (
+ id bigint NOT NULL auto_increment,
+ post_id bigint default NULL,
+ post_date timestamp NOT NULL,
+ activity_summary varchar(4000) default NULL,
+ feed_user_id varchar(255) NOT NULL,
+ activity_type varchar(255) NOT NULL,
+ activity_format varchar(10) default NULL,
+ site_network varchar(255) default NULL,
+ app_tool varchar(36) default NULL,
+ post_user_id varchar(255) NOT NULL,
+ feed_date timestamp NOT NULL,
+ PRIMARY KEY (id),
+ KEY postdate_idx (post_date),
+ KEY feeduserid_idx (feed_user_id),
+ KEY postuserid_idx (post_user_id),
+ KEY sitenetwork_idx (site_network),
+ KEY activityformat_idx (activity_format)
+) type=InnoDB;
+
+
+CREATE TABLE alf_activity_feed_control (
+ id bigint NOT NULL auto_increment,
+ feed_user_id varchar(255) NOT NULL,
+ site_network varchar(255) NOT NULL,
+ app_tool varchar(36) NOT NULL,
+ last_modified timestamp NOT NULL,
+ PRIMARY KEY (id),
+ KEY feedcontroluserid_idx (feed_user_id)
+) type=InnoDB;
diff --git a/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.Oracle9Dialect/create-activities-tables.sql b/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.Oracle9Dialect/create-activities-tables.sql
new file mode 100644
index 0000000000..34b9f389ff
--- /dev/null
+++ b/config/alfresco/dbscripts/create/3.0/org.hibernate.dialect.Oracle9Dialect/create-activities-tables.sql
@@ -0,0 +1,65 @@
+--
+-- Title: Activities Schema
+-- Database: Oracle
+-- Since: V3.0.0 Schema
+--
+-- Note: The Activities schema is NOT managed by Hibernate
+--
+
+
+CREATE TABLE alf_activity_post (
+ sequence_id number(19,0) NOT NULL,
+ post_date timestamp NOT NULL,
+ status varchar2(10) NOT NULL,
+ activity_data varchar2(4000) NOT NULL,
+ post_user_id varchar2(255) NOT NULL,
+ job_task_node number(19,0) NOT NULL,
+ site_network varchar2(255) default NULL,
+ app_tool varchar2(36) default NULL,
+ activity_type varchar2(255) NOT NULL,
+ last_modified timestamp NOT NULL,
+ primary key (sequence_id)
+);
+
+CREATE SEQUENCE alf_activity_post_seq START WITH 1 INCREMENT BY 1;
+
+CREATE INDEX jobtasknode_idx on alf_activity_post(job_task_node);
+CREATE INDEX status_idx on alf_activity_post(status);
+
+
+CREATE TABLE alf_activity_feed (
+ id number(19,0) NOT NULL,
+ post_id number(19,0) default NULL,
+ post_date timestamp NOT NULL,
+ activity_summary varchar2(4000) default NULL,
+ feed_user_id varchar2(255) default NULL,
+ activity_type varchar2(255) NOT NULL,
+ activity_format varchar2(10) default NULL,
+ site_network varchar2(255) default NULL,
+ app_tool varchar2(36) default NULL,
+ post_user_id varchar2(255) NOT NULL,
+ feed_date timestamp NOT NULL,
+ primary key (id)
+);
+
+CREATE SEQUENCE alf_activity_feed_seq START WITH 1 INCREMENT BY 1;
+
+CREATE INDEX postdate_idx ON alf_activity_feed(post_date);
+CREATE INDEX feeduserid_idx ON alf_activity_feed(feed_user_id);
+CREATE INDEX postuserid_idx ON alf_activity_feed(post_user_id);
+CREATE INDEX sitenetwork_idx ON alf_activity_feed(site_network);
+CREATE INDEX activityformat_idx ON alf_activity_feed(activity_format);
+
+
+CREATE TABLE alf_activity_feed_control (
+ id number(19,0) NOT NULL,
+ feed_user_id varchar2(255) NOT NULL,
+ site_network varchar2(255) NOT NULL,
+ app_tool varchar2(36) NOT NULL,
+ last_modified timestamp NOT NULL,
+ primary key (id)
+);
+
+CREATE SEQUENCE alf_activity_feed_control_seq START WITH 1 INCREMENT BY 1;
+
+CREATE INDEX feedcontroluserid_idx ON alf_activity_feed_control(feed_user_id);
diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml
index 9bb9b99495..a8020fd571 100644
--- a/config/alfresco/patch/patch-services-context.xml
+++ b/config/alfresco/patch/patch-services-context.xml
@@ -1468,4 +1468,15 @@
+
+ patch.db-V3.0-0-CreateActivitiesTables
+ patch.schemaUpgradeScript.description
+ 0
+ 125
+ 126
+
+ classpath:alfresco/dbscripts/create/3.0/${db.script.dialect}/create-activities-tables.sql
+
+
+
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index 48de829525..7690195da7 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -205,3 +205,6 @@ avm.remote.idlestream.timeout=30000
# ECM content usages/quotas
system.usages.enabled=true
+
+# Repository endpoint - used by Activity Service
+repo.remote.endpoint.url=http://localhost:8080/alfresco/service
\ No newline at end of file
diff --git a/config/alfresco/scheduled-jobs-context.xml b/config/alfresco/scheduled-jobs-context.xml
index 0297ce19dc..6de99529e2 100644
--- a/config/alfresco/scheduled-jobs-context.xml
+++ b/config/alfresco/scheduled-jobs-context.xml
@@ -354,4 +354,120 @@
+
+
+
+
+
+ org.alfresco.repo.activities.feed.cleanup.FeedCleanupJob
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+ 10
+
+
+
+
+
+ org.alfresco.repo.activities.feed.FeedGeneratorJob
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 30000
+
+
+
+
+
+
+
+ org.alfresco.repo.activities.post.lookup.PostLookupJob
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+ 15000
+
+
+
+
+
+
+
+ org.alfresco.repo.activities.post.cleanup.PostCleanupJob
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+
+
+ 10
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/script-services-context.xml b/config/alfresco/script-services-context.xml
index 48520eb01b..263c3e3799 100644
--- a/config/alfresco/script-services-context.xml
+++ b/config/alfresco/script-services-context.xml
@@ -167,4 +167,13 @@
+
+
+ activities
+
+
+
+
+
+
diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties
index 366ad9ab30..d4b663dcce 100644
--- a/config/alfresco/version.properties
+++ b/config/alfresco/version.properties
@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
-version.schema=125
+version.schema=126
diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java
new file mode 100644
index 0000000000..d0ca31c9b3
--- /dev/null
+++ b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java
@@ -0,0 +1,511 @@
+/*
+ * 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.activities;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.activities.feed.ActivityFeedDAO;
+import org.alfresco.repo.activities.feed.ActivityFeedDaoService;
+import org.alfresco.repo.activities.feed.FeedGenerator;
+import org.alfresco.repo.activities.feed.control.FeedControlDAO;
+import org.alfresco.repo.activities.feed.control.FeedControlDaoService;
+import org.alfresco.repo.activities.post.ActivityPostDAO;
+import org.alfresco.repo.activities.post.ActivityPostDaoService;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.service.cmr.activities.ActivityService;
+import org.alfresco.service.cmr.activities.FeedControl;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AuthorityService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.JSONtoFmModel;
+import org.alfresco.util.ParameterCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.json.JSONException;
+
+/**
+ * Activity Service Implementation
+ *
+ * @author janv
+ */
+public class ActivityServiceImpl implements ActivityService
+{
+ private static final Log logger = LogFactory.getLog(ActivityServiceImpl.class);
+
+ private static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id
+ private static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network
+ private static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type
+ private static final int MAX_LEN_ACTIVITY_DATA = 4000; // needs to match schema: activity_data
+ private static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool
+
+ private ActivityPostDaoService postDaoService;
+ private ActivityFeedDaoService feedDaoService;
+ private FeedControlDaoService feedControlDaoService;
+ private AuthorityService authorityService;
+ private FeedGenerator feedGenerator;
+
+ private int maxFeedItems = 100;
+
+ private boolean userNamesAreCaseSensitive = false;
+
+ public void setMaxFeedItems(int maxFeedItems)
+ {
+ this.maxFeedItems = maxFeedItems;
+ }
+
+ public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive)
+ {
+ this.userNamesAreCaseSensitive = userNamesAreCaseSensitive;
+ }
+
+ public void setPostDaoService(ActivityPostDaoService postDaoService)
+ {
+ this.postDaoService = postDaoService;
+ }
+
+ public void setFeedDaoService(ActivityFeedDaoService feedDaoService)
+ {
+ this.feedDaoService = feedDaoService;
+ }
+
+ public void setFeedControlDaoService(FeedControlDaoService feedControlDaoService)
+ {
+ this.feedControlDaoService = feedControlDaoService;
+ }
+
+ public void setAuthorityService(AuthorityService authorityService)
+ {
+ this.authorityService = authorityService;
+ }
+
+ public void setFeedGenerator(FeedGenerator feedGenerator)
+ {
+ this.feedGenerator = feedGenerator;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void postActivity(String activityType, String network, String appTool, String activityData)
+ {
+ postActivity(activityType, network, appTool, activityData, ActivityPostDAO.STATUS.POSTED);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef)
+ */
+ public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef)
+ {
+ ParameterCheck.mandatory("nodeRef", nodeRef);
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append("}");
+
+ postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
+ */
+ public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef, String name)
+ {
+ ParameterCheck.mandatory("nodeRef", nodeRef);
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",")
+ .append("\"name\":\"").append(name).append("\"")
+ .append("}");
+
+ postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef)
+ */
+ public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef)
+ {
+ // primarily for delete node activities - eg. delete document, delete folder
+
+ ParameterCheck.mandatory("nodeRef", nodeRef);
+ ParameterCheck.mandatory("typeQName", typeQName);
+ ParameterCheck.mandatory("parentNodeRef", parentNodeRef);
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",")
+ .append("\"name\":\"").append(name).append("\"").append(",")
+ .append("\"typeQName\":\"").append(typeQName.toPrefixString()).append("\"").append(",") // TODO toPrefixString does not return prefix ??!!
+ .append("\"parentNodeRef\":\"").append(parentNodeRef.toString()).append("\"")
+ .append("}");
+
+ postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING);
+ }
+
+ private void postActivity(String activityType, String siteNetwork, String appTool, String activityData, ActivityPostDAO.STATUS status)
+ {
+ String currentUser = AuthenticationUtil.getCurrentUserName();
+
+ try
+ {
+ // optional - default to empty string
+ if (siteNetwork == null)
+ {
+ siteNetwork = "";
+ }
+ else if (siteNetwork.length() > MAX_LEN_SITE_ID)
+ {
+ throw new AlfrescoRuntimeException("Invalid site network - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteNetwork);
+ }
+
+ // optional - default to empty string
+ if (appTool == null)
+ {
+ appTool = "";
+ }
+ else if (appTool.length() > MAX_LEN_APP_TOOL_ID)
+ {
+ throw new AlfrescoRuntimeException("Invalid app tool - exceeds " + MAX_LEN_APP_TOOL_ID + " chars: " + appTool);
+ }
+
+ // required
+ ParameterCheck.mandatoryString("activityType", activityType);
+
+ if (activityType.length() > MAX_LEN_ACTIVITY_TYPE)
+ {
+ throw new AlfrescoRuntimeException("Invalid activity type - exceeds " + MAX_LEN_ACTIVITY_TYPE + " chars: " + activityType);
+ }
+
+ // optional - default to empty string
+ if (activityData == null)
+ {
+ activityData = "";
+ }
+ else if (activityType.length() > MAX_LEN_ACTIVITY_DATA)
+ {
+ throw new AlfrescoRuntimeException("Invalid activity data - exceeds " + MAX_LEN_ACTIVITY_DATA + " chars: " + activityData);
+ }
+
+ // required
+ ParameterCheck.mandatoryString("currentUser", currentUser);
+
+ if (currentUser.length() > MAX_LEN_USER_ID)
+ {
+ throw new AlfrescoRuntimeException("Invalid user - exceeds " + MAX_LEN_USER_ID + " chars: " + currentUser);
+ }
+ else if ((! currentUser.equals(AuthenticationUtil.SYSTEM_USER_NAME)) && (! userNamesAreCaseSensitive))
+ {
+ // user names are case-insensitive
+ currentUser = currentUser.toLowerCase();
+ }
+ }
+ catch (AlfrescoRuntimeException e)
+ {
+ // log error and throw exception
+ logger.error(e);
+ throw e;
+ }
+
+ try
+ {
+ Date postDate = new Date();
+ ActivityPostDAO activityPost = new ActivityPostDAO();
+ activityPost.setUserId(currentUser);
+ activityPost.setSiteNetwork(siteNetwork);
+ activityPost.setAppTool(appTool);
+ activityPost.setActivityData(activityData);
+ activityPost.setActivityType(activityType);
+ activityPost.setPostDate(postDate);
+ activityPost.setStatus(status.toString());
+ activityPost.setLastModified(postDate);
+
+ // hash the userid to generate a job task node
+ int nodeCount = feedGenerator.getEstimatedGridSize();
+ int userHashCode = currentUser.hashCode();
+ int nodeHash = (userHashCode % nodeCount) + 1;
+
+ activityPost.setJobTaskNode(nodeHash);
+
+ try
+ {
+ long postId = postDaoService.insertPost(activityPost);
+
+ if (logger.isDebugEnabled())
+ {
+ activityPost.setId(postId);
+ logger.debug("Posted: " + activityPost);
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new AlfrescoRuntimeException("Failed to post activity: " + e, e);
+ }
+ }
+ catch (AlfrescoRuntimeException e)
+ {
+ // log error, subsume exception
+ logger.error(e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public List