Initial Subscription Cervice check-in

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28425 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Florian Mü
2011-06-16 11:57:04 +00:00
parent 2445f01771
commit a9eb35e67f
31 changed files with 2426 additions and 127 deletions

View File

@@ -16,6 +16,7 @@
<import resource="classpath:alfresco/preference-service-context.xml"/>
<import resource="classpath:alfresco/swf-transform-context.xml"/>
<import resource="classpath:alfresco/form-services-context.xml"/>
<import resource="classpath:alfresco/subscription-service-context.xml" />
<import resource="classpath:alfresco/cmis-api-context.xml" />
<import resource="classpath:alfresco/opencmis-context.xml" />
<import resource="classpath:alfresco/period-type-context.xml" />

View File

@@ -142,6 +142,7 @@
<ref bean="patch.db-V4.0-AclChangeSet" />
<ref bean="patch.db-V4.0-NodeAssoc-Ordering" />
<ref bean="patch.db-V4.0-Node-Locale" />
<ref bean="patch.db-V4.0-SubscriptionTables" />
</list>
</property>
</bean>

View File

@@ -41,6 +41,13 @@
<property name="sqlSessionTemplate" ref="activitiesSqlSessionTemplate"/>
</bean>
<bean id="subscriptionsDAO" class="org.alfresco.repo.domain.subscriptions.ibatis.SubscriptionsDAOImpl">
<property name="sqlSessionTemplate" ref="repoSqlSessionTemplate"/>
<property name="personService" ref="personService"/>
<property name="nodeDAO" ref="nodeDAO"/>
<property name="QNameDAO" ref="qnameDAO"/>
</bean>
<bean id="patchDAO" class="org.alfresco.util.bean.HierarchicalBeanLoader">
<property name="targetBeanName">
<value>patchDAO.#bean.dialect#</value>

View File

@@ -0,0 +1,28 @@
--
-- Title: Subscription tables
-- Database: MySQL InnoDB
-- Since: V4.0 Schema 5011
-- Author: Florian Mueller
--
-- Please contact support@alfresco.com if you need assistance with the upgrade.
--
CREATE TABLE alf_subscriptions (
user_node_id BIGINT NOT NULL,
node_id BIGINT NOT NULL,
PRIMARY KEY (user_node_id, node_id),
CONSTRAINT fk_alf_sub_user FOREIGN KEY (user_node_id) REFERENCES alf_node(id) ON DELETE CASCADE,
CONSTRAINT fk_alf_sub_node FOREIGN KEY (node_id) REFERENCES alf_node(id) ON DELETE CASCADE
) ENGINE=InnoDB;
--
-- Record script finish
--
DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.0-SubscriptionTables';
INSERT INTO alf_applied_patch
(id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report)
VALUES
(
'patch.db-V4.0-SubscriptionTables', 'Manually executed script upgrade V4.0: Subscription Tables',
0, 5010, -1, 5011, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed'
);

View File

@@ -0,0 +1,28 @@
--
-- Title: Subscription tables
-- Database: PostgreSQL
-- Since: V4.0 Schema 5011
-- Author: Florian Mueller
--
-- Please contact support@alfresco.com if you need assistance with the upgrade.
--
CREATE TABLE alf_subscriptions (
user_node_id INT8 NOT NULL,
node_id INT8 NOT NULL,
PRIMARY KEY (user_node_id, node_id),
CONSTRAINT fk_alf_sub_user FOREIGN KEY (user_node_id) REFERENCES alf_node(id) ON DELETE CASCADE,
CONSTRAINT fk_alf_sub_node FOREIGN KEY (node_id) REFERENCES alf_node(id) ON DELETE CASCADE
);
--
-- Record script finish
--
DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.0-SubscriptionTables';
INSERT INTO alf_applied_patch
(id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report)
VALUES
(
'patch.db-V4.0-SubscriptionTables', 'Manually executed script upgrade V4.0: Subscription Tables',
0, 5010, -1, 5011, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed'
);

View File

@@ -137,6 +137,11 @@ Inbound settings from iBatis
<typeAlias alias="TestOne" type="org.alfresco.repo.domain.query.CannedQueryDAOTest$TestOneParams"/>
<typeAlias alias="Ids" type="org.alfresco.ibatis.IdsEntity"/>
<!-- Subscriptions -->
<typeAlias alias="Subscription" type="org.alfresco.repo.domain.subscriptions.SubscriptionEntity"/>
<typeAlias alias="SubscriptionNode" type="org.alfresco.repo.domain.subscriptions.SubscriptionNodeEntity"/>
</typeAliases>
<typeHandlers>
@@ -168,6 +173,7 @@ Inbound settings from iBatis
<mapper resource="alfresco/ibatis/#resource.dialect#/usage-insert-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/avm-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/avm-insert-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/subscriptions-common-SqlMap.xml"/>
<!-- Canned queries -->
<mapper resource="alfresco/ibatis/#resource.dialect#/query-test-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-usages-common-SqlMap.xml"/>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="alfresco.subscriptions">
<!-- -->
<!-- Result Maps -->
<!-- -->
<resultMap id="result_SubscriptionNode" type="SubscriptionNode">
<result property="protocol" column="protocol" jdbcType="VARCHAR" javaType="java.lang.String"/>
<result property="identifier" column="identifier" jdbcType="VARCHAR" javaType="java.lang.String"/>
<result property="id" column="uuid" jdbcType="VARCHAR" javaType="java.lang.String"/>
</resultMap>
<!-- -->
<!-- Parameter Maps -->
<!-- -->
<parameterMap id="pararmeter_Subscription" type="Subscription">
<parameter property="userNodeId" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="nodeId" jdbcType="BIGINT" javaType="java.lang.Long"/>
</parameterMap>
<!-- -->
<!-- SQL Snippets -->
<!-- -->
<!-- -->
<!-- Insert, Delete -->
<!-- -->
<insert id="insert_Subscription" parameterMap="pararmeter_Subscription">
insert into alf_subscriptions (user_node_id, node_id)
values (?, ?)
</insert>
<delete id="delete_Subscription" parameterMap="pararmeter_Subscription">
delete from alf_subscriptions where user_node_id = #{userNodeId} and node_id = #{nodeId}
</delete>
<!-- -->
<!-- Statements -->
<!-- -->
<select id="select_Subscriptions" parameterType="map" resultMap="result_SubscriptionNode">
select
store.protocol as protocol, store.identifier as identifier, node.uuid as uuid
from
alf_node node
join alf_store store on (store.id = node.store_id)
join alf_subscriptions sub on (sub.node_id = node.id)
where
node.node_deleted = #{false} and sub.user_node_id = #{userNodeId}
</select>
<!-- Get the ... -->
<select id="select_countSubscriptions" parameterType="map" resultType="long">
select
count(node_id)
from
alf_node node join alf_subscriptions sub on (sub.node_id = node.id)
where
node.node_deleted = #{false} and sub.user_node_id = #{userNodeId}
</select>
<select id="select_hasSubscribed" parameterMap="pararmeter_Subscription" resultType="long">
select
count(node_id)
from
alf_node node join alf_subscriptions sub on (sub.node_id = node.id)
where
node.node_deleted = #{false} and sub.user_node_id = #{userNodeId} and sub.node_id = #{nodeId}
</select>
<select id="select_Following" parameterType="map" resultType="String">
select
props.string_value as user_id
from
alf_node node
join alf_subscriptions sub on (sub.node_id = node.id)
join alf_node_properties props on (props.node_id = node.id)
where
node.node_deleted = #{false} and props.qname_id=#{userIdQname} and sub.user_node_id = #{userNodeId}
</select>
<select id="select_Followers" parameterType="map" resultType="String">
select
props.string_value as user_id
from
alf_node node
join alf_subscriptions sub on (sub.user_node_id = node.id)
join alf_node_properties props on (props.node_id = node.id)
where
node.node_deleted = #{false} and props.qname_id=#{userIdQname} and sub.node_id = #{userNodeId}
</select>
<select id="select_countFollowers" parameterType="map" resultType="long">
select
count(user_node_id)
from
alf_node node join alf_subscriptions sub on (sub.user_node_id = node.id)
where
node.node_deleted = #{false} and sub.node_id = #{userNodeId}
</select>
</mapper>

View File

@@ -268,6 +268,10 @@
<type>d:boolean</type>
</property>
<property name="cm:subscriptionsPrivate">
<type>d:boolean</type>
</property>
<!-- system maintained / protected values -->
<property name="cm:emailFeedId">

View File

@@ -2871,4 +2871,15 @@
</property>
</bean>
<bean id="patch.db-V4.0-SubscriptionTables" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
<property name="id"><value>patch.db-V4.0-SubscriptionTables</value></property>
<property name="description"><value>patch.schemaUpgradeScript.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>5010</value></property>
<property name="targetSchema"><value>5011</value></property>
<property name="scriptUrl">
<value>classpath:alfresco/dbscripts/create/${db.script.dialect}/AlfrescoCreate-SubscriptionTables.sql</value>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,29 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<!-- Subscription service bean -->
<bean id="SubscriptionService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.subscriptions.SubscriptionService</value>
</property>
<property name="target">
<ref bean="subscriptionService"/>
</property>
<property name="interceptorNames">
<list>
<idref bean="AuditMethodInterceptor"/>
<idref bean="exceptionTranslator"/>
</list>
</property>
</bean>
<!-- Subscription implementation bean -->
<bean id="subscriptionService" class="org.alfresco.repo.subscriptions.SubscriptionServiceImpl" >
<property name="subscriptionsDAO" ref="subscriptionsDAO" />
<property name="nodeService" ref="nodeService" />
<property name="personService" ref="personService" />
<property name="activityService" ref="activityService" />
</bean>
</beans>

View File

@@ -86,6 +86,7 @@
<property name="nodeService" ref="NodeService"/>
<property name="contentService" ref="ContentService"/>
<property name="permissionService" ref="PermissionService"/>
<property name="subscriptionService" ref="SubscriptionService"/>
<property name="templateSearchPaths">
<list>

View File

@@ -235,7 +235,7 @@ public class ActivityPostServiceImpl implements ActivityPostService
private String getCurrentUser()
{
String userId = AuthenticationUtil.getFullyAuthenticatedUser();
String userId = AuthenticationUtil.getRunAsUser();
if ((userId != null) && (! userId.equals(AuthenticationUtil.SYSTEM_USER_NAME)) && (! userNamesAreCaseSensitive))
{
// user names are not case-sensitive

View File

@@ -32,4 +32,6 @@ public interface ActivityType
public final String SITE_GROUP_ADDED = "org.alfresco.site.group-added";
public final String SITE_GROUP_REMOVED = "org.alfresco.site.group-removed";
public final String SITE_GROUP_ROLE_UPDATE = "org.alfresco.site.group-role-changed";
public final String SUBSCRIPTIONS_SUBSCRIBE = "org.alfresco.subscriptions.subscribed";
public final String SUBSCRIPTIONS_FOLLOW = "org.alfresco.subscriptions.followed";
}

View File

@@ -64,6 +64,7 @@ public abstract class FeedTaskProcessor
{
private static final Log logger = LogFactory.getLog(FeedTaskProcessor.class);
public static final String FEED_FORMAT_JSON = "json";
public static final String FEED_FORMAT_ATOMENTRY = "atomentry";
public static final String FEED_FORMAT_HTML = "html";
@@ -117,6 +118,7 @@ public abstract class FeedTaskProcessor
Map<String, List<String>> activityTemplates = new HashMap<String, List<String>>(10);
Map<String, Set<String>> siteConnectedUsers = new TreeMap<String, Set<String>>();
Map<String, Set<String>> followers = new TreeMap<String, Set<String>>();
Map<String, List<FeedControlEntity>> userFeedControls = new HashMap<String, List<FeedControlEntity>>();
Map<String, Template> templateCache = new TreeMap<String, Template>();
@@ -207,15 +209,6 @@ public abstract class FeedTaskProcessor
String thisSite = (activityPost.getSiteNetwork() != null ? activityPost.getSiteNetwork() : "");
if (thisSite.length() == 0)
{
// note: although we allow posts without site id - we currently require site context to generate feeds for site members (hence skip here with warning)
// (also Share currently only posts activities within site context)
logger.warn(">>> Skipping activity post " + activityPost.getId() + " since no site");
updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.PROCESSED);
continue;
}
model.put(ActivityFeedEntity.KEY_ACTIVITY_FEED_TYPE, activityPost.getActivityType());
model.put(ActivityFeedEntity.KEY_ACTIVITY_FEED_SITE, thisSite);
model.put("userId", activityPost.getUserId());
@@ -224,25 +217,60 @@ public abstract class FeedTaskProcessor
model.put("xmldate", new ISO8601DateFormatMethod());
model.put("repoEndPoint", ctx.getRepoEndPoint());
// Get the members of this site - save hammering the repository by reusing cached site members
Set<String> connectedUsers = siteConnectedUsers.get(thisSite);
if (connectedUsers == null)
// Recipients of this post
Set<String> recipients = new HashSet<String>();
// Add site members to recipient list
if (thisSite.length() > 0)
{
// Get the members of this site - save hammering the repository by reusing cached site members
Set<String> connectedUsers = siteConnectedUsers.get(thisSite);
if (connectedUsers == null)
{
try
{
// Repository callback to get site members
connectedUsers = getSiteMembers(ctx, thisSite);
connectedUsers.add(""); // add empty posting userid - to represent site feed !
}
catch(Exception e)
{
logger.error("Skipping activity post " + activityPost.getId() + " since failed to get site members: " + e);
updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR);
continue;
}
// Cache them for future use in this same invocation
siteConnectedUsers.put(thisSite, connectedUsers);
}
recipients.addAll(connectedUsers);
}
// Add followers to recipient list
Set<String> followerUsers = followers.get(activityPost.getUserId());
if(followerUsers == null) {
try
{
// Repository callback to get site members
connectedUsers = getSiteMembers(ctx, thisSite);
connectedUsers.add(""); // add empty posting userid - to represent site feed !
followerUsers = getFollowers(activityPost.getUserId());
}
catch(Exception e)
{
logger.error("Skipping activity post " + activityPost.getId() + " since failed to get site members: " + e);
logger.error("Skipping activity post " + activityPost.getId() + " since failed to get followers: " + e);
updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR);
continue;
}
// Cache them for future use in this same invocation
siteConnectedUsers.put(thisSite, connectedUsers);
followers.put(activityPost.getUserId(), followerUsers);
}
recipients.addAll(followerUsers);
if(recipients.size() == 0) {
if (logger.isDebugEnabled())
{
logger.debug("No recipients for activity post " + activityPost.getId() + ".");
}
return;
}
try
@@ -251,22 +279,22 @@ public abstract class FeedTaskProcessor
if (logger.isTraceEnabled())
{
logger.trace("Process: " + connectedUsers.size() + " candidate connections for activity post " + activityPost.getId());
logger.trace("Process: " + recipients.size() + " candidate connections for activity post " + activityPost.getId());
}
int excludedConnections = 0;
for (String connectedUser : connectedUsers)
for (String recipient : recipients)
{
List<FeedControlEntity> feedControls = null;
if (! connectedUser.equals(""))
if (! recipient.equals(""))
{
// Get user's feed controls
feedControls = userFeedControls.get(connectedUser);
feedControls = userFeedControls.get(recipient);
if (feedControls == null)
{
feedControls = getFeedControls(connectedUser);
userFeedControls.put(connectedUser, feedControls);
feedControls = getFeedControls(recipient);
userFeedControls.put(recipient, feedControls);
}
}
@@ -278,7 +306,7 @@ public abstract class FeedTaskProcessor
else
{
// read permission check
if (! canRead(ctx, connectedUser, model))
if (! canRead(ctx, recipient, model))
{
excludedConnections++;
continue;
@@ -306,7 +334,7 @@ public abstract class FeedTaskProcessor
ActivityFeedEntity feed = new ActivityFeedEntity();
// Generate activity feed summary
feed.setFeedUserId(connectedUser);
feed.setFeedUserId(recipient);
feed.setPostUserId(postingUserId);
feed.setActivityType(activityType);
@@ -356,7 +384,7 @@ public abstract class FeedTaskProcessor
if (logger.isDebugEnabled())
{
logger.debug("Processed: " + (connectedUsers.size() - excludedConnections) + " connections for activity post " + activityPost.getId() + " (excluded " + excludedConnections + ")");
logger.debug("Processed: " + (recipients.size() - excludedConnections) + " connections for activity post " + activityPost.getId() + " (excluded " + excludedConnections + ")");
}
}
finally
@@ -398,7 +426,6 @@ public abstract class FeedTaskProcessor
public abstract int updatePostStatus(long id, ActivityPostEntity.STATUS status) throws SQLException;
protected String callWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException
{
URL url = new URL(urlString);
@@ -482,6 +509,8 @@ public abstract class FeedTaskProcessor
return members;
}
protected abstract Set<String> getFollowers(String userId) throws Exception;
protected boolean canRead(RepoCtx ctx, final String connectedUser, Map<String, Object> model) throws Exception
{
throw new UnsupportedOperationException("FeedTaskProcessor: Remote callback for 'canRead' not implemented");

View File

@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.activities.feed.FeedTaskProcessor;
import org.alfresco.repo.activities.feed.RepoCtx;
import org.alfresco.repo.activities.post.lookup.PostLookup;
@@ -46,6 +47,8 @@ import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.SubscriptionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
@@ -74,6 +77,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
private NodeService nodeService;
private ContentService contentService;
private PermissionService permissionService;
private SubscriptionService subscriptionService;
private String defaultEncoding;
private List<String> templateSearchPaths;
@@ -115,6 +119,11 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
this.permissionService = permissionService;
}
public void setSubscriptionService(SubscriptionService subscriptionService)
{
this.subscriptionService = subscriptionService;
}
public void setDefaultEncoding(String defaultEncoding)
{
this.defaultEncoding = defaultEncoding;
@@ -152,7 +161,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
public void endTransaction() throws SQLException
{
// NOOP
// NOOP
}
public List<ActivityPostEntity> selectPosts(ActivityPostEntity selector) throws SQLException
@@ -172,7 +181,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
public List<FeedControlEntity> selectUserFeedControls(String userId) throws SQLException
{
return feedControlDAO.selectFeedControls(userId);
return feedControlDAO.selectFeedControls(userId);
}
@Override
@@ -199,7 +208,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
{
for (String userName : mapResult.keySet())
{
if (! ctx.isUserNamesAreCaseSensitive())
if (!ctx.isUserNamesAreCaseSensitive())
{
userName = userName.toLowerCase();
}
@@ -229,10 +238,10 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
return true;
}
String nodeRefStr = (String)model.get(PostLookup.JSON_NODEREF);
String nodeRefStr = (String) model.get(PostLookup.JSON_NODEREF);
if (nodeRefStr == null)
{
nodeRefStr = (String)model.get(PostLookup.JSON_NODEREF_PARENT);
nodeRefStr = (String) model.get(PostLookup.JSON_NODEREF_PARENT);
}
if (nodeRefStr != null)
@@ -269,7 +278,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
{
// TODO: require ghosting - this is temp workaround (we should not rely on archive - may be permanently deleted, ie. not archived or already purged)
NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, nodeRef.getId());
if (! nodeService.exists(archiveNodeRef))
if (!nodeService.exists(archiveNodeRef))
{
return false;
}
@@ -308,7 +317,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
{
if (logger.isDebugEnabled())
{
logger.debug("canRead: " + nodeRef + " in "+(System.currentTimeMillis()-start)+" msecs");
logger.debug("canRead: " + nodeRef + " in " + (System.currentTimeMillis() - start) + " msecs");
}
}
}
@@ -336,7 +345,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
if (idx != -1)
{
path = subPath.substring(0, idx);
templatePattern = subPath.substring(idx+1) + ".ftl";
templatePattern = subPath.substring(idx + 1) + ".ftl";
}
}
@@ -376,12 +385,12 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
path = "/";
}
if (! path.startsWith("/"))
if (!path.startsWith("/"))
{
path = "/" + path;
}
if (! path.endsWith("/"))
if (!path.endsWith("/"))
{
path = path + "/";
}
@@ -437,4 +446,21 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica
}
return documentPaths;
}
protected Set<String> getFollowers(String userId) throws Exception
{
Set<String> result = new HashSet<String>();
if (!subscriptionService.isSubscriptionListPrivate(userId))
{
PagingFollowingResults fr = subscriptionService.getFollowers(userId, new PagingRequest(1000000, null));
if (fr.getPage() != null)
{
result.addAll(fr.getPage());
}
}
return result;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.subscriptions;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResults;
import org.alfresco.service.cmr.subscriptions.SubscriptionItemTypeEnum;
public abstract class AbstractSubscriptionsDAO implements SubscriptionsDAO
{
protected NodeDAO nodeDAO;
protected PersonService personService;
public final void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
}
public final void setPersonService(PersonService personService)
{
this.personService = personService;
}
@Override
public abstract PagingSubscriptionResults selectSubscriptions(String userId, SubscriptionItemTypeEnum type,
PagingRequest pagingRequest);
@Override
public abstract int countSubscriptions(String userId, SubscriptionItemTypeEnum type);
@Override
public abstract void insertSubscription(String userId, NodeRef node);
@Override
public abstract void deleteSubscription(String userId, NodeRef node);
@Override
public abstract boolean hasSubscribed(String userId, NodeRef node);
@Override
public abstract PagingFollowingResults selectFollowers(String userId, PagingRequest pagingRequest);
@Override
public abstract int countFollowers(String userId);
protected NodeRef getUserNodeRef(String userId)
{
return personService.getPerson(userId, false);
}
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.subscriptions;
import junit.framework.TestCase;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResults;
import org.alfresco.service.cmr.subscriptions.SubscriptionItemTypeEnum;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
public class SubscriptionDAOTest extends TestCase
{
private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
private RetryingTransactionHelper txnHelper;
private PersonService personService;
private SubscriptionsDAO subscriptionsDAO;
protected NodeRef getUserNodeRef(final String userId)
{
final PersonService ps = personService;
return AuthenticationUtil.runAs(new RunAsWork<NodeRef>()
{
@Override
public NodeRef doWork() throws Exception
{
return ps.getPerson(userId);
}
}, AuthenticationUtil.getSystemUserName());
}
protected void insert(final String userId, final NodeRef node) throws Exception
{
RetryingTransactionCallback<Object> callback = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
subscriptionsDAO.insertSubscription(userId, node);
return null;
}
};
txnHelper.doInTransaction(callback, false, false);
}
protected void delete(final String userId, final NodeRef node) throws Exception
{
RetryingTransactionCallback<Object> callback = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
subscriptionsDAO.deleteSubscription(userId, node);
return null;
}
};
txnHelper.doInTransaction(callback, false, false);
}
protected int count(final String userId) throws Exception
{
RetryingTransactionCallback<Integer> callback = new RetryingTransactionCallback<Integer>()
{
public Integer execute() throws Throwable
{
return subscriptionsDAO.countSubscriptions(userId, SubscriptionItemTypeEnum.USER);
}
};
return txnHelper.doInTransaction(callback, false, false);
}
protected boolean hasSubscribed(final String userId, final NodeRef node) throws Exception
{
RetryingTransactionCallback<Boolean> callback = new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Throwable
{
return subscriptionsDAO.hasSubscribed(userId, node);
}
};
return txnHelper.doInTransaction(callback, false, false);
}
protected PagingSubscriptionResults select(final String userId) throws Exception
{
RetryingTransactionCallback<PagingSubscriptionResults> callback = new RetryingTransactionCallback<PagingSubscriptionResults>()
{
public PagingSubscriptionResults execute() throws Throwable
{
return subscriptionsDAO.selectSubscriptions(userId, SubscriptionItemTypeEnum.USER, new PagingRequest(
100000, null));
}
};
return txnHelper.doInTransaction(callback, false, false);
}
protected int countFollowers(final String userId) throws Exception
{
RetryingTransactionCallback<Integer> callback = new RetryingTransactionCallback<Integer>()
{
public Integer execute() throws Throwable
{
return subscriptionsDAO.countFollowers(userId);
}
};
return txnHelper.doInTransaction(callback, false, false);
}
protected PagingFollowingResults selectFollowing(final String userId) throws Exception
{
RetryingTransactionCallback<PagingFollowingResults> callback = new RetryingTransactionCallback<PagingFollowingResults>()
{
public PagingFollowingResults execute() throws Throwable
{
return subscriptionsDAO.selectFollowing(userId, new PagingRequest(100000, null));
}
};
return txnHelper.doInTransaction(callback, false, false);
}
protected PagingFollowingResults selectFollowers(final String userId) throws Exception
{
RetryingTransactionCallback<PagingFollowingResults> callback = new RetryingTransactionCallback<PagingFollowingResults>()
{
public PagingFollowingResults execute() throws Throwable
{
return subscriptionsDAO.selectFollowers(userId, new PagingRequest(100000, null));
}
};
return txnHelper.doInTransaction(callback, false, false);
}
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
transactionService = serviceRegistry.getTransactionService();
txnHelper = transactionService.getRetryingTransactionHelper();
personService = serviceRegistry.getPersonService();
subscriptionsDAO = (SubscriptionsDAO) ctx.getBean("subscriptionsDAO");
}
public void testInsertAndDelete() throws Exception
{
String userId = "admin";
String userId2 = "guest";
NodeRef nodeRef = getUserNodeRef(userId2);
// check subscription first
if (hasSubscribed(userId, nodeRef))
{
delete(userId, nodeRef);
}
boolean hasSubscribed = hasSubscribed(userId, nodeRef);
assertFalse(hasSubscribed);
// count subscriptions
int count = count(userId);
assertTrue(count >= 0);
// insert
insert(userId, nodeRef);
insert(userId, nodeRef);
assertEquals(count + 1, count(userId));
assertTrue(hasSubscribed(userId, nodeRef));
// select
PagingSubscriptionResults psr = select(userId);
assertNotNull(psr);
assertNotNull(psr.getPage());
assertTrue(psr.getPage().contains(nodeRef));
PagingFollowingResults following = selectFollowing(userId);
assertNotNull(following);
assertNotNull(following.getPage());
assertTrue(following.getPage().contains(userId2));
assertEquals(psr.getPage().size(), following.getPage().size());
// count followers
int followerCount = countFollowers(userId2);
assertTrue(followerCount >= 0);
// select followers
PagingFollowingResults followers = selectFollowers(userId2);
assertNotNull(followers);
assertNotNull(followers.getPage());
assertTrue(followers.getPage().contains(userId));
// delete
delete(userId, nodeRef);
assertEquals(count, count(userId));
assertFalse(hasSubscribed(userId, nodeRef));
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.subscriptions;
public class SubscriptionEntity
{
private Long userNodeId;
private Long nodeId;
public Long getUserNodeId()
{
return userNodeId;
}
public void setUserNodeId(Long userNodeId)
{
this.userNodeId = userNodeId;
}
public Long getNodeId()
{
return nodeId;
}
public void setNodeId(Long nodeId)
{
this.nodeId = nodeId;
}
public boolean getFalse()
{
return false;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.subscriptions;
import org.alfresco.service.cmr.repository.NodeRef;
public class SubscriptionNodeEntity
{
private String protocol;
private String identifier;
private String id;
public String getProtocol()
{
return protocol;
}
public void setProtocol(String protocol)
{
this.protocol = protocol;
}
public String getIdentifier()
{
return identifier;
}
public void setIdentifier(String identifier)
{
this.identifier = identifier;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public NodeRef getNodeRef()
{
return new NodeRef(protocol, identifier, id);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.subscriptions;
import org.alfresco.query.PagingRequest;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResults;
import org.alfresco.service.cmr.subscriptions.SubscriptionItemTypeEnum;
public interface SubscriptionsDAO
{
PagingSubscriptionResults selectSubscriptions(String userId, SubscriptionItemTypeEnum type,
PagingRequest pagingRequest);
int countSubscriptions(String userId, SubscriptionItemTypeEnum type);
void insertSubscription(String userId, NodeRef node);
void deleteSubscription(String userId, NodeRef node);
boolean hasSubscribed(String userId, NodeRef node);
PagingFollowingResults selectFollowing(String userId, PagingRequest pagingRequest);
PagingFollowingResults selectFollowers(String userId, PagingRequest pagingRequest);
int countFollowers(String userId);
}

View File

@@ -0,0 +1,293 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.subscriptions.ibatis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.subscriptions.AbstractSubscriptionsDAO;
import org.alfresco.repo.domain.subscriptions.SubscriptionEntity;
import org.alfresco.repo.domain.subscriptions.SubscriptionNodeEntity;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResultsImpl;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResults;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResultsImpl;
import org.alfresco.service.cmr.subscriptions.SubscriptionItemTypeEnum;
import org.alfresco.util.Pair;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
public class SubscriptionsDAOImpl extends AbstractSubscriptionsDAO
{
private SqlSessionTemplate template;
private QNameDAO qnameDAO;
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
this.template = sqlSessionTemplate;
}
public final void setQNameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
@Override
public PagingSubscriptionResults selectSubscriptions(String userId, SubscriptionItemTypeEnum type,
PagingRequest pagingRequest)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
if (type == null)
{
throw new IllegalArgumentException("Type may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userNodeId", userPair.getFirst());
map.put("false", Boolean.FALSE);
@SuppressWarnings("unchecked")
List<SubscriptionNodeEntity> nodeList = (List<SubscriptionNodeEntity>) template.selectList(
"alfresco.subscriptions.select_Subscriptions", map, new RowBounds(pagingRequest.getSkipCount(),
pagingRequest.getMaxItems() + 1));
boolean hasMore = nodeList.size() > pagingRequest.getMaxItems();
List<NodeRef> result = new ArrayList<NodeRef>(nodeList.size());
for (SubscriptionNodeEntity sne : nodeList)
{
result.add(sne.getNodeRef());
if (result.size() == pagingRequest.getMaxItems())
{
break;
}
}
Integer totalCount = null;
if (pagingRequest.getRequestTotalCountMax() > 0)
{
totalCount = countSubscriptions(userId, type);
}
return new PagingSubscriptionResultsImpl(result, hasMore, totalCount);
}
@Override
public int countSubscriptions(String userId, SubscriptionItemTypeEnum type)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
if (type == null)
{
throw new IllegalArgumentException("Type may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userNodeId", userPair.getFirst());
map.put("false", Boolean.FALSE);
Number count = (Number) template.selectOne("alfresco.subscriptions.select_countSubscriptions", map);
return count.intValue();
}
@Override
public void insertSubscription(String userId, NodeRef node)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
if (node == null)
{
throw new IllegalArgumentException("Node may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(node);
SubscriptionEntity se = new SubscriptionEntity();
se.setUserNodeId(userPair.getFirst());
se.setNodeId(nodePair.getFirst());
if (((Number) template.selectOne("alfresco.subscriptions.select_hasSubscribed", se)).intValue() == 0)
{
template.insert("alfresco.subscriptions.insert_Subscription", se);
}
}
@Override
public void deleteSubscription(String userId, NodeRef node)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
if (node == null)
{
throw new IllegalArgumentException("Node may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(node);
SubscriptionEntity se = new SubscriptionEntity();
se.setUserNodeId(userPair.getFirst());
se.setNodeId(nodePair.getFirst());
template.delete("alfresco.subscriptions.delete_Subscription", se);
}
@Override
public boolean hasSubscribed(String userId, NodeRef node)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
if (node == null)
{
throw new IllegalArgumentException("Node may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(node);
SubscriptionEntity se = new SubscriptionEntity();
se.setUserNodeId(userPair.getFirst());
se.setNodeId(nodePair.getFirst());
return ((Number) template.selectOne("alfresco.subscriptions.select_hasSubscribed", se)).intValue() == 1;
}
@Override
public PagingFollowingResults selectFollowing(String userId, PagingRequest pagingRequest)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userIdQname", qnameDAO.getQName(ContentModel.PROP_USERNAME).getFirst());
map.put("userNodeId", userPair.getFirst());
map.put("false", Boolean.FALSE);
@SuppressWarnings("unchecked")
List<String> userList = (List<String>) template.selectList("alfresco.subscriptions.select_Following", map,
new RowBounds(pagingRequest.getSkipCount(), pagingRequest.getMaxItems() + 1));
boolean hasMore = userList.size() > pagingRequest.getMaxItems();
if (hasMore && userList.size() > 0)
{
userList.remove(userList.size() - 1);
}
Integer totalCount = null;
if (pagingRequest.getRequestTotalCountMax() > 0)
{
totalCount = countFollowers(userId);
}
return new PagingFollowingResultsImpl(userList, hasMore, totalCount);
}
@Override
public PagingFollowingResults selectFollowers(String userId, PagingRequest pagingRequest)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userIdQname", qnameDAO.getQName(ContentModel.PROP_USERNAME).getFirst());
map.put("userNodeId", userPair.getFirst());
map.put("false", Boolean.FALSE);
@SuppressWarnings("unchecked")
List<String> userList = (List<String>) template.selectList("alfresco.subscriptions.select_Followers", map,
new RowBounds(pagingRequest.getSkipCount(), pagingRequest.getMaxItems() + 1));
boolean hasMore = userList.size() > pagingRequest.getMaxItems();
if (hasMore && userList.size() > 0)
{
userList.remove(userList.size() - 1);
}
Integer totalCount = null;
if (pagingRequest.getRequestTotalCountMax() > 0)
{
totalCount = countFollowers(userId);
}
return new PagingFollowingResultsImpl(userList, hasMore, totalCount);
}
@Override
public int countFollowers(String userId)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
NodeRef userNodeRef = getUserNodeRef(userId);
Pair<Long, NodeRef> userPair = nodeDAO.getNodePair(userNodeRef);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userNodeId", userPair.getFirst());
map.put("false", Boolean.FALSE);
Number count = (Number) template.selectOne("alfresco.subscriptions.select_countFollowers", map);
return count.intValue();
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.subscriptions;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.activities.feed.FeedGenerator;
import org.alfresco.repo.activities.feed.local.LocalFeedTaskProcessor;
import org.alfresco.repo.activities.post.lookup.PostLookup;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.subscriptions.SubscriptionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyMap;
import org.quartz.Scheduler;
import org.springframework.context.ApplicationContext;
public class SubscriptionServiceActivitiesTest extends TestCase
{
// Location of activity type templates (for site activities)
// assumes test-resources is on classpath
protected static final String TEST_TEMPLATES_LOCATION = "activities";
protected ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
protected SubscriptionService subscriptionService;
protected PersonService personService;
protected PostLookup postLookup;
protected FeedGenerator feedGenerator;
@Override
public void setUp() throws Exception
{
// Let's shut down the scheduler so that we aren't competing with the
// scheduled versions of the post lookup and
// feed generator jobs
Scheduler scheduler = (Scheduler) ctx.getBean("schedulerFactory");
scheduler.shutdown();
// Get the required services
subscriptionService = (SubscriptionService) ctx.getBean("SubscriptionService");
personService = (PersonService) ctx.getBean("PersonService");
ChildApplicationContextFactory activitiesFeed = (ChildApplicationContextFactory) ctx.getBean("ActivitiesFeed");
ApplicationContext activitiesFeedCtx = activitiesFeed.getApplicationContext();
postLookup = (PostLookup) activitiesFeedCtx.getBean("postLookup");
feedGenerator = (FeedGenerator) activitiesFeedCtx.getBean("feedGenerator");
LocalFeedTaskProcessor feedProcessor = (LocalFeedTaskProcessor) activitiesFeedCtx.getBean("feedTaskProcessor");
List<String> templateSearchPaths = new ArrayList<String>(1);
templateSearchPaths.add(TEST_TEMPLATES_LOCATION);
feedProcessor.setTemplateSearchPaths(templateSearchPaths);
feedProcessor.setUseRemoteCallbacks(false);
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
}
protected void deletePerson(String userId)
{
personService.deletePerson(userId);
}
protected NodeRef createPerson(String userId)
{
deletePerson(userId);
PropertyMap properties = new PropertyMap(5);
properties.put(ContentModel.PROP_USERNAME, userId);
properties.put(ContentModel.PROP_FIRSTNAME, userId);
properties.put(ContentModel.PROP_LASTNAME, "Test");
properties.put(ContentModel.PROP_EMAIL, userId + "@email.com");
return personService.createPerson(properties);
}
protected void generateFeed() throws Exception
{
postLookup.execute();
feedGenerator.execute();
}
public void testFollowingActivity() throws Exception
{
final String userId1 = "bob";
final String userId2 = "tom";
createPerson(userId1);
createPerson(userId2);
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
@Override
public Object doWork() throws Exception
{
subscriptionService.follow(userId1, userId2);
return null;
}
}, userId1);
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
@Override
public Object doWork() throws Exception
{
subscriptionService.follow(userId2, userId1);
return null;
}
}, userId2);
generateFeed();
deletePerson(userId1);
deletePerson(userId2);
}
}

View File

@@ -0,0 +1,386 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.subscriptions;
import java.io.Serializable;
import java.util.Collections;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.activities.ActivityType;
import org.alfresco.repo.domain.subscriptions.SubscriptionsDAO;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.activities.ActivityService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResultsImpl;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResults;
import org.alfresco.service.cmr.subscriptions.PagingSubscriptionResultsImpl;
import org.alfresco.service.cmr.subscriptions.PrivateSubscriptionListException;
import org.alfresco.service.cmr.subscriptions.SubscriptionItemTypeEnum;
import org.alfresco.service.cmr.subscriptions.SubscriptionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
public class SubscriptionServiceImpl implements SubscriptionService
{
/** Logger */
private static Log logger = LogFactory.getLog(SubscriptionServiceImpl.class);
/** Activity tool */
private static final String ACTIVITY_TOOL = "subscriptionService";
/** Activity values */
private static final String SUB_USER = "user";
private static final String SUB_USER_TO_FOLLOW = "userToFollow";
private static final String SUB_NODE = "node";
protected SubscriptionsDAO subscriptionsDAO;
protected NodeService nodeService;
protected PersonService personService;
protected ActivityService activityService;
/**
* Sets the subscriptions DAO.
*/
public void setSubscriptionsDAO(SubscriptionsDAO subscriptionsDAO)
{
this.subscriptionsDAO = subscriptionsDAO;
}
/**
* Sets the node service.
*/
public final void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Sets the person service.
*/
public final void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* Sets the activity service.
*/
public final void setActivityService(ActivityService activictyService)
{
this.activityService = activictyService;
}
@SuppressWarnings("unchecked")
@Override
public PagingSubscriptionResults getSubscriptions(String userId, SubscriptionItemTypeEnum type,
PagingRequest pagingRequest)
{
if (!subscriptionsEnabled())
{
return new PagingSubscriptionResultsImpl(Collections.EMPTY_LIST, false, 0);
}
checkRead(userId);
return subscriptionsDAO.selectSubscriptions(userId, type, pagingRequest);
}
@Override
public int getSubscriptionCount(String userId, SubscriptionItemTypeEnum type)
{
if (!subscriptionsEnabled())
{
return 0;
}
return subscriptionsDAO.countSubscriptions(userId, type);
}
@Override
public void subscribe(String userId, NodeRef node)
{
if (!subscriptionsEnabled())
{
return;
}
checkWrite(userId);
checkUserNode(node);
subscriptionsDAO.insertSubscription(userId, node);
if (userId.equalsIgnoreCase(AuthenticationUtil.getRunAsUser()))
{
String activityDataJSON = null;
try
{
JSONObject activityData = new JSONObject();
activityData.put(SUB_USER, userId);
activityData.put(SUB_NODE, node.toString());
activityDataJSON = activityData.toString();
} catch (JSONException je)
{
// log error, subsume exception
logger.error("Failed to get activity data: " + je);
}
activityService.postActivity(ActivityType.SUBSCRIPTIONS_SUBSCRIBE, null, ACTIVITY_TOOL, activityDataJSON);
}
}
@Override
public void unsubscribe(String userId, NodeRef node)
{
if (!subscriptionsEnabled())
{
return;
}
checkWrite(userId);
subscriptionsDAO.deleteSubscription(userId, node);
}
@Override
public boolean hasSubscribed(String userId, NodeRef node)
{
if (!subscriptionsEnabled())
{
return false;
}
checkRead(userId);
return subscriptionsDAO.hasSubscribed(userId, node);
}
@SuppressWarnings("unchecked")
@Override
public PagingFollowingResults getFollowing(String userId, PagingRequest pagingRequest)
{
if (!subscriptionsEnabled())
{
return new PagingFollowingResultsImpl(Collections.EMPTY_LIST, false, 0);
}
checkRead(userId);
return subscriptionsDAO.selectFollowing(userId, pagingRequest);
}
@SuppressWarnings("unchecked")
@Override
public PagingFollowingResults getFollowers(String userId, PagingRequest pagingRequest)
{
if (!subscriptionsEnabled())
{
return new PagingFollowingResultsImpl(Collections.EMPTY_LIST, false, 0);
}
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
return subscriptionsDAO.selectFollowers(userId, pagingRequest);
}
@Override
public int getFollowersCount(String userId)
{
if (!subscriptionsEnabled())
{
return 0;
}
return subscriptionsDAO.countFollowers(userId);
}
@Override
public int getFollowingCount(String userId)
{
if (!subscriptionsEnabled())
{
return 0;
}
return getSubscriptionCount(userId, SubscriptionItemTypeEnum.USER);
}
@Override
public void follow(String userId, String userToFollow)
{
if (!subscriptionsEnabled())
{
return;
}
checkWrite(userId);
subscriptionsDAO.insertSubscription(userId, getUserNodeRef(userToFollow));
if (userId.equalsIgnoreCase(AuthenticationUtil.getRunAsUser()))
{
String activityDataJSON = null;
try
{
JSONObject activityData = new JSONObject();
activityData.put(SUB_USER, userId);
activityData.put(SUB_USER_TO_FOLLOW, userToFollow);
activityDataJSON = activityData.toString();
} catch (JSONException je)
{
// log error, subsume exception
logger.error("Failed to get activity data: " + je);
}
activityService.postActivity(ActivityType.SUBSCRIPTIONS_FOLLOW, null, ACTIVITY_TOOL, activityDataJSON);
}
}
@Override
public void unfollow(String userId, String userToUnfollow)
{
if (!subscriptionsEnabled())
{
return;
}
checkWrite(userId);
subscriptionsDAO.deleteSubscription(userId, getUserNodeRef(userToUnfollow));
}
@Override
public boolean follows(String userId, String userToFollow)
{
if (!subscriptionsEnabled())
{
return false;
}
checkRead(userId);
return subscriptionsDAO.hasSubscribed(userId, getUserNodeRef(userToFollow));
}
@Override
public void setSubscriptionListPrivate(String userId, boolean isPrivate)
{
checkWrite(userId);
nodeService.setProperty(getUserNodeRef(userId), ContentModel.PROP_SUBSCRIPTIONS_PRIVATE, isPrivate);
}
@Override
public boolean isSubscriptionListPrivate(String userId)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
Serializable privateList = nodeService.getProperty(getUserNodeRef(userId),
ContentModel.PROP_SUBSCRIPTIONS_PRIVATE);
if (privateList == null)
{
return false;
}
if (privateList instanceof Boolean && !((Boolean) privateList).booleanValue())
{
return false;
}
return true;
}
protected boolean subscriptionsEnabled()
{
return true;
}
/**
* Checks if the current user is allowed to get subscription data.
*/
protected void checkRead(String userId)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
String currentUser = AuthenticationUtil.getRunAsUser();
if (currentUser == null)
{
throw new IllegalArgumentException("No current user!");
}
if (currentUser.equalsIgnoreCase(userId) || currentUser.equalsIgnoreCase(AuthenticationUtil.getAdminUserName())
|| AuthenticationUtil.isRunAsUserTheSystemUser() || !isSubscriptionListPrivate(userId))
{
return;
}
throw new PrivateSubscriptionListException("subscription_service.err.private-list");
}
/**
* Checks if the current user is allowed to get change data.
*/
protected void checkWrite(String userId)
{
if (userId == null)
{
throw new IllegalArgumentException("User Id may not be null!");
}
String currentUser = AuthenticationUtil.getRunAsUser();
if (currentUser == null)
{
throw new IllegalArgumentException("No current user!");
}
if (currentUser.equalsIgnoreCase(userId) || currentUser.equalsIgnoreCase(AuthenticationUtil.getAdminUserName())
|| AuthenticationUtil.isRunAsUserTheSystemUser())
{
return;
}
throw new AccessDeniedException("subscription_service.err.write-denied");
}
/**
* Gets the user node ref from the user id.
*/
protected NodeRef getUserNodeRef(String userId)
{
return personService.getPerson(userId, false);
}
/**
* Checks if the node is a user node and throws an exception if it id not.
*/
protected void checkUserNode(NodeRef nodeRef)
{
// we only support user-to-user subscriptions in this release
if (!ContentModel.TYPE_USER.equals(nodeService.getType(nodeRef)))
{
throw new IllegalArgumentException("Only user nodes supported!");
}
}
}

View File

@@ -0,0 +1,241 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.subscriptions;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.subscriptions.PagingFollowingResults;
import org.alfresco.service.cmr.subscriptions.PrivateSubscriptionListException;
import org.alfresco.service.cmr.subscriptions.SubscriptionService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PropertyMap;
import org.springframework.context.ApplicationContext;
public class SubscriptionServiceImplTest extends TestCase
{
public static final String USER_BOB = "bob";
public static final String USER_TOM = "tom";
public static final String USER_LISA = "lisa";
private UserTransaction txn;
protected ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
protected TransactionService transactionService;
protected SubscriptionService subscriptionService;
protected PersonService personService;
@Override
public void setUp() throws Exception
{
// Get the required services
transactionService = (TransactionService) ctx.getBean("TransactionService");
subscriptionService = (SubscriptionService) ctx.getBean("SubscriptionService");
personService = (PersonService) ctx.getBean("PersonService");
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
txn = transactionService.getNonPropagatingUserTransaction(false);
txn.begin();
createPerson(USER_BOB);
createPerson(USER_TOM);
createPerson(USER_LISA);
}
@Override
protected void tearDown() throws Exception
{
deletePerson(USER_BOB);
deletePerson(USER_TOM);
deletePerson(USER_LISA);
if (txn != null)
{
if (txn.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
txn.rollback();
} else
{
txn.commit();
}
txn = null;
}
}
protected void deletePerson(String userId)
{
personService.deletePerson(userId);
}
protected NodeRef createPerson(String userId)
{
deletePerson(userId);
PropertyMap properties = new PropertyMap(5);
properties.put(ContentModel.PROP_USERNAME, userId);
properties.put(ContentModel.PROP_FIRSTNAME, userId);
properties.put(ContentModel.PROP_LASTNAME, "Test");
properties.put(ContentModel.PROP_EMAIL, userId + "@email.com");
return personService.createPerson(properties);
}
public void testFollow() throws Exception
{
String userId1 = USER_BOB;
String userId2 = USER_TOM;
String userId3 = USER_LISA;
// check follows first
if (subscriptionService.follows(userId1, userId2))
{
subscriptionService.unfollow(userId1, userId2);
}
assertFalse(subscriptionService.follows(userId1, userId2));
// count the people user 1 is following
int count = subscriptionService.getFollowingCount(userId1);
assertTrue(count >= 0);
// user 1 follows user 2 -- twice (the second follow request should be
// ignored)
subscriptionService.follow(userId1, userId2);
subscriptionService.follow(userId1, userId2);
assertEquals(count + 1, subscriptionService.getFollowingCount(userId1));
assertTrue(subscriptionService.follows(userId1, userId2));
// user 1 follows user 3
subscriptionService.follow(userId1, userId3);
assertEquals(count + 2, subscriptionService.getFollowingCount(userId1));
assertTrue(subscriptionService.follows(userId1, userId3));
// get following list of user 1
PagingFollowingResults following = subscriptionService.getFollowing(userId1, new PagingRequest(100000, null));
assertNotNull(following);
assertNotNull(following.getPage());
assertTrue(following.getPage().contains(userId2));
assertTrue(following.getPage().contains(userId3));
// count followers of user 2
int followerCount = subscriptionService.getFollowersCount(userId2);
assertTrue(followerCount > 0);
// get followers of user 2
PagingFollowingResults followers = subscriptionService.getFollowers(userId2, new PagingRequest(100000, null));
assertNotNull(followers);
assertNotNull(followers.getPage());
assertTrue(followers.getPage().contains(userId1));
// unfollow
subscriptionService.unfollow(userId1, userId2);
assertEquals(count + 1, subscriptionService.getFollowingCount(userId1));
assertFalse(subscriptionService.follows(userId1, userId2));
assertTrue(subscriptionService.follows(userId1, userId3));
subscriptionService.unfollow(userId1, userId3);
assertEquals(count, subscriptionService.getFollowingCount(userId1));
assertFalse(subscriptionService.follows(userId1, userId3));
}
public void testDeletePerson() throws Exception
{
String userId1 = USER_BOB;
String userId2 = "subscription-temp-user";
createPerson(userId2);
subscriptionService.follow(userId1, userId2);
assertTrue(subscriptionService.follows(userId1, userId2));
deletePerson(userId2);
PagingFollowingResults following = subscriptionService.getFollowing(userId1, new PagingRequest(100000, null));
assertNotNull(following);
assertNotNull(following.getPage());
assertFalse(following.getPage().contains(userId2));
}
public void testPrivateList() throws Exception
{
final String userId1 = USER_BOB;
final String userId2 = USER_TOM;
assertFalse(subscriptionService.isSubscriptionListPrivate(userId1));
subscriptionService.setSubscriptionListPrivate(userId1, false);
assertFalse(subscriptionService.isSubscriptionListPrivate(userId1));
subscriptionService.setSubscriptionListPrivate(userId1, true);
assertTrue(subscriptionService.isSubscriptionListPrivate(userId1));
subscriptionService.setSubscriptionListPrivate(userId1, false);
assertFalse(subscriptionService.isSubscriptionListPrivate(userId1));
subscriptionService.setSubscriptionListPrivate(userId1, true);
assertTrue(subscriptionService.isSubscriptionListPrivate(userId1));
subscriptionService.follow(userId1, userId2);
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
@Override
public Object doWork() throws Exception
{
assertNotNull(subscriptionService.getFollowing(userId1, new PagingRequest(100000, null)));
return null;
}
}, userId1);
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
@Override
public Object doWork() throws Exception
{
assertNotNull(subscriptionService.getFollowing(userId1, new PagingRequest(100000, null)));
return null;
}
}, AuthenticationUtil.getAdminUserName());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
@Override
public Object doWork() throws Exception
{
try
{
subscriptionService.getFollowing(userId1, new PagingRequest(100000, null));
fail("Expected PrivateSubscriptionListException!");
} catch (PrivateSubscriptionListException psle)
{
// expected
}
return null;
}
}, userId2);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
import org.alfresco.query.PagingResults;
/**
* Response object for follower or following paging requests.
*
* @author Florian Mueller
* @since 4.0
*/
public interface PagingFollowingResults extends PagingResults<String>
{
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
import java.util.Collections;
import java.util.List;
import org.alfresco.util.Pair;
public class PagingFollowingResultsImpl implements PagingFollowingResults
{
private List<String> page;
private boolean hasMore;
private Pair<Integer, Integer> totalCount;
public PagingFollowingResultsImpl(List<String> page, boolean hasMore, Integer total)
{
this.page = page;
this.hasMore = hasMore;
if (total != null)
{
totalCount = new Pair<Integer, Integer>(total, total);
}
}
@Override
public List<String> getPage()
{
return Collections.unmodifiableList(page);
}
@Override
public boolean hasMoreItems()
{
return hasMore;
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
{
return totalCount;
}
@Override
public String getQueryExecutionId()
{
return null;
}
@Override
public boolean permissionsApplied()
{
return false;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
import org.alfresco.query.PagingResults;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Response object for subscription paging requests.
*
* @author Florian Mueller
* @since 4.0
*/
public interface PagingSubscriptionResults extends PagingResults<NodeRef>
{
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
import java.util.Collections;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
public class PagingSubscriptionResultsImpl implements PagingSubscriptionResults
{
private List<NodeRef> page;
private boolean hasMore;
private Pair<Integer, Integer> totalCount;
public PagingSubscriptionResultsImpl(List<NodeRef> page, boolean hasMore, Integer total)
{
this.page = page;
this.hasMore = hasMore;
if (total != null)
{
totalCount = new Pair<Integer, Integer>(total, total);
}
}
@Override
public List<NodeRef> getPage()
{
return Collections.unmodifiableList(page);
}
@Override
public boolean hasMoreItems()
{
return hasMore;
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
{
return totalCount;
}
@Override
public String getQueryExecutionId()
{
return null;
}
@Override
public boolean permissionsApplied()
{
return false;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* This exception is thrown if a subscription list is private and the accessing
* user is not allowed to see it.
*
* @author Florian Mueller
* @since 4.0
*/
public class PrivateSubscriptionListException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = 6971869799749343887L;
public PrivateSubscriptionListException(String msg)
{
super(msg);
}
public PrivateSubscriptionListException(String msg, Throwable cause)
{
super(msg, cause);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
/**
* Subscription types enum.
*
* @author Florian Mueller
* @since 4.0
*/
public enum SubscriptionItemTypeEnum
{
USER("user");
private String value;
SubscriptionItemTypeEnum(String type)
{
value = type;
}
public String getValue()
{
return value;
}
public static SubscriptionItemTypeEnum fromValue(String v)
{
for (SubscriptionItemTypeEnum ste : SubscriptionItemTypeEnum.values())
{
if (ste.value.equals(v))
{
return ste;
}
}
throw new IllegalArgumentException(v);
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.subscriptions;
import org.alfresco.query.PagingRequest;
import org.alfresco.service.Auditable;
import org.alfresco.service.NotAuditable;
import org.alfresco.service.PublicService;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Subscription Service.
*
* @author Florian Mueller
* @since 4.0
*/
@PublicService
public interface SubscriptionService
{
// --- subscription ---
/**
* Returns the nodes a user has subscribed to.
*
* @param userId
* the id of the user
* @param type
* the type of the nodes
* @param pagingRequest
* paging details
*
* @throws PrivateSubscriptionListException
* if the subscription list is private and the calling user is
* not allowed to see it
*/
@NotAuditable
PagingSubscriptionResults getSubscriptions(String userId, SubscriptionItemTypeEnum type, PagingRequest pagingRequest);
/**
* Returns how many nodes the given user has subscribed to.
*
* @param userId
* the id of the user
* @param type
* the type of the nodes
*/
@NotAuditable
int getSubscriptionCount(String userId, SubscriptionItemTypeEnum type);
/**
* Subscribes to a node.
*
* @param userId
* id of the user
* @param node
* the node
*/
@Auditable(parameters = { "userId", "node" })
void subscribe(String userId, NodeRef node);
/**
* Unsubscribes from a node.
*
* @param userId
* id of the user
* @param node
* the node
*/
@Auditable(parameters = { "userId", "node" })
void unsubscribe(String userId, NodeRef node);
/**
* Returns if the user has subscribed to the given node.
*
* @param userId
* id of the user
* @param node
* the node
*/
@NotAuditable
boolean hasSubscribed(String userId, NodeRef node);
// --- follow ---
/**
* Returns a list of users that the given user follows.
*
* @param userId
* id of the user
* @param pagingRequest
* paging details
* @throws PrivateSubscriptionListException
* if the subscription list is private and the calling user is
* not allowed to see it
*/
@NotAuditable
PagingFollowingResults getFollowing(String userId, PagingRequest pagingRequest);
/**
* Returns a list of users that follow the given user.
*
* @param userId
* id of the user
* @param pagingRequest
* paging details
*/
@NotAuditable
PagingFollowingResults getFollowers(String userId, PagingRequest pagingRequest);
/**
* Returns how many users the given user follows.
*
* @param userId
* the id of the user
* @param type
* the type of the nodes
*/
@NotAuditable
int getFollowingCount(String userId);
/**
* Returns how many users follow the given user.
*
* @param userId
* the id of the user
* @param type
* the type of the nodes
*/
@NotAuditable
int getFollowersCount(String userId);
/**
* Follows another
*
* @param userId
* the id of the user
* @param userToFollow
* the id of the user to follow
*/
@Auditable(parameters = { "userId", "userToFollow" })
void follow(String userId, String userToFollow);
@Auditable(parameters = { "userId", "userToUnfollow" })
void unfollow(String userId, String userToUnfollow);
/**
* Returns if the user follows to the given other user.
*
* @param userId
* id of the user
* @param userToFollow
* the id of the other user
*/
@NotAuditable
boolean follows(String userId, String userToFollow);
// --- privacy settings ---
/**
* Sets or unsets the subscription list of the given user to private.
*
* @param userId
* the id of the user
* @param isPrivate
* <code>true</code> - set list private,
* <code>false<code> - set list public
*
*/
@Auditable(parameters = { "userId", "isPrivate" })
void setSubscriptionListPrivate(String userId, boolean isPrivate);
/**
* Returns if the subscription list of the given user is set to private.
*
* @param userId
* the id of the user
*/
@NotAuditable
boolean isSubscriptionListPrivate(String userId);
}