diff --git a/config/alfresco/application-context-highlevel.xml b/config/alfresco/application-context-highlevel.xml index d53053cb80..fc48eb0691 100644 --- a/config/alfresco/application-context-highlevel.xml +++ b/config/alfresco/application-context-highlevel.xml @@ -19,5 +19,6 @@ + diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index 78cb98dede..493eaf5df7 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -20,12 +20,10 @@ - - @@ -43,12 +41,32 @@ - + + + patchDAO.#bean.dialect# + + + org.alfresco.repo.domain.patch.PatchDAO + + + org.hibernate.dialect.Dialect + + + + + + + + + + + + diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql index b282dc6698..52ead45470 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql @@ -253,60 +253,6 @@ CREATE TABLE alf_locale UNIQUE KEY locale_str (locale_str) ) ENGINE=InnoDB; -CREATE TABLE alf_attributes -( - id BIGINT NOT NULL AUTO_INCREMENT, - type VARCHAR(1) NOT NULL, - version BIGINT NOT NULL, - acl_id BIGINT, - bool_value BIT, - byte_value TINYINT, - short_value SMALLINT, - int_value INTEGER, - long_value BIGINT, - float_value FLOAT, - double_value DOUBLE PRECISION, - string_value TEXT, - serializable_value BLOB, - PRIMARY KEY (id), - KEY fk_alf_attr_acl (acl_id), - CONSTRAINT fk_alf_attr_acl FOREIGN KEY (acl_id) REFERENCES alf_access_control_list (id) -) ENGINE=InnoDB; - -CREATE TABLE alf_global_attributes -( - name VARCHAR(160) NOT NULL, - attribute BIGINT, - PRIMARY KEY (name), - UNIQUE KEY attribute (attribute), - KEY fk_alf_gatt_att (attribute), - CONSTRAINT fk_alf_gatt_att FOREIGN KEY (attribute) REFERENCES alf_attributes (id) -) ENGINE=InnoDB; - -CREATE TABLE alf_list_attribute_entries -( - list_id BIGINT NOT NULL, - mindex INTEGER NOT NULL, - attribute_id BIGINT, - PRIMARY KEY (list_id, mindex), - KEY fk_alf_lent_att (attribute_id), - KEY fk_alf_lent_latt (list_id), - CONSTRAINT fk_alf_lent_att FOREIGN KEY (attribute_id) REFERENCES alf_attributes (id), - CONSTRAINT fk_alf_lent_latt FOREIGN KEY (list_id) REFERENCES alf_attributes (id) -) ENGINE=InnoDB; - -CREATE TABLE alf_map_attribute_entries -( - map_id BIGINT NOT NULL, - mkey VARCHAR(160) NOT NULL, - attribute_id BIGINT, - PRIMARY KEY (map_id, mkey), - KEY fk_alf_matt_matt (map_id), - KEY fk_alf_matt_att (attribute_id), - CONSTRAINT fk_alf_matt_att FOREIGN KEY (attribute_id) REFERENCES alf_attributes (id), - CONSTRAINT fk_alf_matt_matt FOREIGN KEY (map_id) REFERENCES alf_attributes (id) -) ENGINE=InnoDB; - CREATE TABLE alf_node_aspects ( node_id BIGINT NOT NULL, diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql index b36241634e..7422996a08 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql @@ -270,61 +270,6 @@ CREATE TABLE alf_locale ); CREATE UNIQUE INDEX locale_str ON alf_locale (locale_str); -CREATE SEQUENCE alf_attributes_seq START WITH 1 INCREMENT BY 1; -CREATE TABLE alf_attributes -( - id INT8 NOT NULL, - type VARCHAR(1) NOT NULL, - version INT8 NOT NULL, - acl_id INT8, - bool_value BOOL, - byte_value INT2, - short_value INT4, - int_value INT4, - long_value INT8, - float_value FLOAT4, - double_value FLOAT8, - string_value VARCHAR(1024), - serializable_value BYTEA, - PRIMARY KEY (id), - CONSTRAINT fk_alf_attr_acl FOREIGN KEY (acl_id) REFERENCES alf_access_control_list (id) -); -CREATE INDEX fk_alf_attr_acl ON alf_attributes (acl_id); - -CREATE TABLE alf_global_attributes -( - name VARCHAR(160) NOT NULL, - attribute INT8, - PRIMARY KEY (name), - CONSTRAINT fk_alf_gatt_att FOREIGN KEY (attribute) REFERENCES alf_attributes (id) -); -CREATE UNIQUE INDEX attribute ON alf_global_attributes (attribute); -CREATE INDEX fk_alf_gatt_att ON alf_global_attributes (attribute); - -CREATE TABLE alf_list_attribute_entries -( - list_id INT8 NOT NULL, - mindex INT4 NOT NULL, - attribute_id INT8, - PRIMARY KEY (list_id, mindex), - CONSTRAINT fk_alf_lent_att FOREIGN KEY (attribute_id) REFERENCES alf_attributes (id), - CONSTRAINT fk_alf_lent_latt FOREIGN KEY (list_id) REFERENCES alf_attributes (id) -); -CREATE INDEX fk_alf_lent_att ON alf_list_attribute_entries (attribute_id); -CREATE INDEX fk_alf_lent_latt ON alf_list_attribute_entries (list_id); - -CREATE TABLE alf_map_attribute_entries -( - map_id INT8 NOT NULL, - mkey VARCHAR(160) NOT NULL, - attribute_id INT8, - PRIMARY KEY (map_id, mkey), - CONSTRAINT fk_alf_matt_att FOREIGN KEY (attribute_id) REFERENCES alf_attributes (id), - CONSTRAINT fk_alf_matt_matt FOREIGN KEY (map_id) REFERENCES alf_attributes (id) -); -CREATE INDEX fk_alf_matt_matt ON alf_map_attribute_entries (map_id); -CREATE INDEX fk_alf_matt_att ON alf_map_attribute_entries (attribute_id); - CREATE TABLE alf_node_aspects ( node_id INT8 NOT NULL, diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml index 9d37766315..f18bced93c 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml @@ -4,6 +4,8 @@ + + @@ -53,7 +55,7 @@ insert into alf_activity_feed_control (id, feed_user_id, site_network, app_tool, last_modified) - values (#{id}, #{feedUserId}, #{siteNetwork}, #{appTool}, #{lastModified}) + values (#{id}, #{feedUserId}, #{siteNetwork,jdbcType=VARCHAR}, #{appTool,jdbcType=VARCHAR}, #{lastModified}) @@ -63,7 +65,7 @@ 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}) + values (#{id}, #{activityType}, #{activitySummary,jdbcType=VARCHAR}, #{activitySummaryFormat,jdbcType=VARCHAR}, #{feedUserId,jdbcType=VARCHAR}, #{postUserId}, #{postDate}, #{postId,jdbcType=BIGINT}, #{siteNetwork,jdbcType=VARCHAR}, #{appTool,jdbcType=VARCHAR}, #{feedDate}) @@ -73,7 +75,7 @@ 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}) + values (#{id}, #{status}, #{activityData}, #{userId}, #{postDate}, #{activityType}, #{siteNetwork,jdbcType=VARCHAR}, #{appTool,jdbcType=VARCHAR}, #{jobTaskNode}, #{lastModified}) @@ -307,7 +309,7 @@ #{status} ]]> diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/appliedpatch-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/appliedpatch-common-SqlMap.xml index 5e4d7d8f60..14b2c8a94a 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/appliedpatch-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/appliedpatch-common-SqlMap.xml @@ -17,7 +17,7 @@ - + @@ -41,7 +41,7 @@ ( #{id}, #{description,jdbcType=VARCHAR}, #{fixesFromSchema,jdbcType=INTEGER}, #{fixesToSchema,jdbcType=INTEGER}, #{targetSchema,jdbcType=INTEGER}, - #{appliedToSchema,jdbcType=INTEGER}, #{appliedOnDate,jdbcType=DATE}, #{appliedToServer,jdbcType=VARCHAR}, + #{appliedToSchema,jdbcType=INTEGER}, #{appliedOnDate,jdbcType=TIMESTAMP}, #{appliedToServer,jdbcType=VARCHAR}, #{wasExecuted,jdbcType=BOOLEAN}, #{succeeded,jdbcType=BOOLEAN}, #{report,jdbcType=VARCHAR} ) @@ -59,7 +59,7 @@ fixes_to_schema = #{fixesToSchema,jdbcType=INTEGER}, target_schema = #{targetSchema,jdbcType=INTEGER}, applied_to_schema = #{appliedToSchema,jdbcType=INTEGER}, - applied_on_date = #{appliedOnDate,jdbcType=DATE}, + applied_on_date = #{appliedOnDate,jdbcType=TIMESTAMP}, applied_to_server = #{appliedToServer,jdbcType=VARCHAR}, was_executed = #{wasExecuted,jdbcType=BOOLEAN}, succeeded = #{succeeded,jdbcType=BOOLEAN}, diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml index a03fabcb69..d0938cb04c 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml @@ -4,10 +4,12 @@ + + - + @@ -79,7 +81,7 @@ insert into alf_audit_entry (id, audit_app_id, audit_user_id, audit_time, audit_values_id) - values (#{id}, #{auditApplicationId}, #{auditUserId}, #{auditTime}, #{auditValuesId}) + values (#{id}, #{auditApplicationId}, #{auditUserId,jdbcType=BIGINT}, #{auditTime}, #{auditValuesId,jdbcType=BIGINT}) diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml index 16c2f3d2e1..65964bb2ce 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml @@ -240,7 +240,7 @@ insert into avm_version_roots (id, avm_store_id, root_id, version_id, creator, create_date, tag, description) - values (#{id}, #{storeId}, #{rootNodeId}, #{version}, #{creator}, #{createdDate}, #{tag}, #{description}) + values (#{id}, #{storeId}, #{rootNodeId}, #{version}, #{creator}, #{createdDate}, #{tag,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}) @@ -852,8 +852,8 @@ update avm_version_roots set - tag = #{tag}, - description = #{description} + tag = #{tag,jdbcType=VARCHAR}, + description = #{description,jdbcType=VARCHAR} where id = #{id} diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml index ce22fefeab..75f9db6528 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml @@ -466,7 +466,7 @@ join alf_node node on node.acl_id = acl.id join alf_child_assoc priChild on priChild.parent_node_id = node.id and is_primary = #{trueOrFalse} join alf_node child on priChild.child_node_id = child.id - join alf_access_control_list childAcl on childAcl.id = child.acl_id AND childAcl.inherits = #trueOrFalse# + join alf_access_control_list childAcl on childAcl.id = child.acl_id AND childAcl.inherits = #{trueOrFalse} where ( childAcl.id is not null @@ -580,24 +580,28 @@ ) + + drop table alf_list_attribute_entries + + + + drop table alf_map_attribute_entries + + + + drop table alf_global_attributes + + + + drop table alf_attributes + + + + drop sequence alf_attributes_seq + + - - delete from alf_list_attribute_entries - - - - delete from alf_map_attribute_entries - - - - delete from alf_global_attributes - - - - delete from alf_attributes - - \ No newline at end of file diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 6141b06c23..3e057e4aa7 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -375,8 +375,8 @@ patch.migrateAttrPropBackedBeans.result=Processed {0} attributes ({1} properties patch.migrateAttrChainingURS.description=Migrate old Chaining User Registry Synchronizer attributes patch.migrateAttrChainingURS.result=Processed {0} attributes -patch.migrateAttrDelete.description=Delete old attributes (if any) after they have been migrated -patch.migrateAttrDelete.result=Old attributes were deleted (if any) +patch.migrateAttrDropOldTables.description=Drops old alf_*attribute* tables and sequence +patch.migrateAttrDropOldTables.result=Drop tables alf_attributes, alf_global_attributes, alf_list_attribute_entries, alf_map_attribute_entries and sequence alf_attributes_seq patch.transfer.targetrulefolder.description=Creates the transfer target rule folder for the default transfer group. diff --git a/config/alfresco/node-locator-context.xml b/config/alfresco/node-locator-context.xml new file mode 100644 index 0000000000..6db4647e93 --- /dev/null +++ b/config/alfresco/node-locator-context.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index cef03ed0c5..9965dce8fc 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -2272,7 +2272,6 @@ - patch.migrateAttrAVMLocks patch.migrateAttrAVMLocks.description @@ -2293,7 +2292,6 @@ - patch.migrateAttrPropBackedBeans patch.migrateAttrPropBackedBeans.description @@ -2313,7 +2311,6 @@ - patch.migrateAttrChainingURS patch.migrateAttrChainingURS.description @@ -2333,13 +2330,19 @@ - - + patch.migrateAttrDelete patch.migrateAttrDelete.description 0 4106 4107 + + + patch.migrateAttrDropOldTables + patch.migrateAttrDropOldTables.description + 0 + 5006 + 5007 false @@ -2579,12 +2582,11 @@ patch.db-V3.4-AVM-rename-dupes patch.schemaUpgradeScript.description 0 - 4200 - 4201 + 5005 + 5006 classpath:alfresco/dbscripts/upgrade/3.4/${db.script.dialect}/AVM-rename-dupes.sql - @@ -2596,8 +2598,8 @@ patch.activitiesEmailTemplate patch.activitiesEmailTemplate.description 0 - 4300 - 4301 + 5005 + 5006 @@ -2622,8 +2624,8 @@ patch.newUserEmailTemplates patch.newUserEmailTemplates.description 0 - 4301 - 4302 + 5005 + 5006 @@ -2640,8 +2642,8 @@ patch.inviteEmailTemplates patch.inviteEmailTemplates.description 0 - 4301 - 4302 + 5005 + 5006 @@ -2658,8 +2660,8 @@ patch.htmlNotificationMailTemplates patch.htmlNotificationMailTemplates.description 0 - 4301 - 4302 + 5005 + 5006 @@ -2676,8 +2678,8 @@ patch.imapSpacesLocaleTemplates patch.imapSpacesLocaleTemplates.description 0 - 4302 - 4303 + 5005 + 5006 @@ -2759,8 +2761,8 @@ - - + + classpath:alfresco/dbscripts/upgrade/3.4/${db.script.dialect}/varchar-field-sizes-quadruple-increasing.sql @@ -2770,9 +2772,8 @@ patch.fixAclInheritance patch.fixAclInheritance.description 0 - 4304 - 4305 - + 5005 + 5006 @@ -2791,8 +2792,8 @@ patch.db-V3.4-JBPM-FK-indexes patch.schemaUpgradeScript.description 0 - 4305 - 4306 + 5005 + 5006 classpath:alfresco/dbscripts/create/${db.script.dialect}/AlfrescoPostCreate-JBPM-FK-indexes.sql @@ -2802,8 +2803,8 @@ patch.imap.clear.old.messages patch.imap.clear.old.messages.description 0 - 4202 - 4203 + 5005 + 5006 @@ -2813,8 +2814,8 @@ patch.imap.clear.old.messages patch.imap.clear.old.messages.description 0 - 5000 - 5001 + 5005 + 5006 @@ -2824,8 +2825,8 @@ patch.imapSpacesLocaleTemplates patch.imapSpacesLocaleTemplates.description 0 - 5000 - 5001 + 5005 + 5006 diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 062aa7a905..da1729d3b2 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -633,11 +633,12 @@ PROPAGATION_NOT_SUPPORTED, readOnly PROPAGATION_NOT_SUPPORTED, readOnly PROPAGATION_NOT_SUPPORTED, readOnly - PROPAGATION_NOT_SUPPORTED, readOnly PROPAGATION_NOT_SUPPORTED, readOnly PROPAGATION_NOT_SUPPORTED, readOnly PROPAGATION_NOT_SUPPORTED, readOnly - ${server.transaction.mode.default} + + PROPAGATION_REQUIRED, readOnly + ${server.transaction.mode.default} diff --git a/config/alfresco/site-services-context.xml b/config/alfresco/site-services-context.xml index e39cd5e34d..8e83f0c93f 100644 --- a/config/alfresco/site-services-context.xml +++ b/config/alfresco/site-services-context.xml @@ -19,7 +19,7 @@ - org.alfresco.service.cmr.site.SiteService + org.alfresco.repo.site.SiteServiceInternal diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 1f6d414257..0f500c0478 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=5001 +version.schema=5007 diff --git a/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrDeletePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrDropOldTablesPatch.java similarity index 57% rename from source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrDeletePatch.java rename to source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrDropOldTablesPatch.java index 6d4534280a..6b89ce0a65 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrDeletePatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/MigrateAttrDropOldTablesPatch.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,26 +18,19 @@ */ package org.alfresco.repo.admin.patch.impl; -import java.util.List; - -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.domain.patch.PatchDAO; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; /** - * Migrate attributes - check no custom attributes and then delete from 'alf_*attribute*' tables + * Migrate attributes (drop tables 'alf_*attribute*') * - * @author janv - * @since 3.4 + * @author Derek Hulley + * @since 4.0 */ -public class MigrateAttrDeletePatch extends AbstractPatch +public class MigrateAttrDropOldTablesPatch extends AbstractPatch { - private Log logger = LogFactory.getLog(this.getClass()); - - private static final String MSG_SUCCESS = "patch.migrateAttrDelete.result"; + private static final String MSG_SUCCESS = "patch.migrateAttrDropOldTables.result"; private PatchDAO patchDAO; @@ -49,18 +42,7 @@ public class MigrateAttrDeletePatch extends AbstractPatch @Override protected String applyInternal() throws Exception { - List results = patchDAO.getOldAttrCustomNames(); - - if (results.size() > 0) - { - for (String custom : results) - { - logger.warn("Custom global attribute found: "+custom); - } - throw new AlfrescoRuntimeException("Custom attributes found - will require custom migration patch: "+results); - } - - patchDAO.deleteAllOldAttrs(); + patchDAO.migrateOldAttrDropTables(); // build the result message String msg = I18NUtil.getMessage(MSG_SUCCESS); diff --git a/source/java/org/alfresco/repo/avm/AVMNodeService.java b/source/java/org/alfresco/repo/avm/AVMNodeService.java index 18ccf9b1ac..ea16e50ff6 100644 --- a/source/java/org/alfresco/repo/avm/AVMNodeService.java +++ b/source/java/org/alfresco/repo/avm/AVMNodeService.java @@ -1096,7 +1096,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi /** * TODO: Implement */ - public boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef) + public boolean removeSecondaryChildAssociation(ChildAssociationRef childAssocRef) { throw new UnsupportedOperationException(); } diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index a03be2f531..dbb0f9873b 100644 --- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -1122,6 +1122,9 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl assoc.setChildNodeTypeQNameIds(new ArrayList(childNodeTypeQNameIds)); ChildAssocResultHandler resultHandler = new ChildAssocResultHandler(resultsCallback); + // TODO MyBatis workaround - see also http://code.google.com/p/mybatis/issues/detail?id=58 (and #139, #234, ...) + template.clearCache(); + template.select(SELECT_CHILD_ASSOCS_OF_PARENT, assoc, resultHandler); resultsCallback.done(); } @@ -1144,6 +1147,10 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl } ChildAssocResultHandler resultHandler = new ChildAssocResultHandler(resultsCallback); + + // TODO MyBatis workaround - see also http://code.google.com/p/mybatis/issues/detail?id=58 (and #139, #234, ...) + template.clearCache(); + template.select(SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_PARENT_ASSOCS_OF_TYPE, assoc, resultHandler); resultsCallback.done(); } diff --git a/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java b/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java index db0324691d..31be0e3cbd 100644 --- a/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java @@ -27,7 +27,6 @@ import org.alfresco.repo.domain.contentdata.ContentDataDAO; import org.alfresco.service.cmr.repository.ContentData; import org.apache.ibatis.session.ResultHandler; - /** * Abstract implementation for Patch DAO. *

@@ -217,6 +216,7 @@ public abstract class AbstractPatchDAOImpl implements PatchDAO, BatchingDAO protected abstract int deleteAclMemberEntitiesForAcls(List aclIds); // note: caller's row handler is expected to migrate the attrs + @Override public void migrateOldAttrTenants(ResultHandler resultHandler) { getOldAttrTenantsImpl(resultHandler); @@ -225,6 +225,7 @@ public abstract class AbstractPatchDAOImpl implements PatchDAO, BatchingDAO protected abstract void getOldAttrTenantsImpl(ResultHandler resultHandler); // note: caller's row handler is expected to migrate the attrs + @Override public void migrateOldAttrAVMLocks(ResultHandler resultHandler) { getOldAttrAVMLocksImpl(resultHandler); @@ -233,6 +234,7 @@ public abstract class AbstractPatchDAOImpl implements PatchDAO, BatchingDAO protected abstract void getOldAttrAVMLocksImpl(ResultHandler resultHandler); // note: caller's row handler is expected to migrate the attrs + @Override public void migrateOldAttrPropertyBackedBeans(ResultHandler resultHandler) { getOldAttrPropertyBackedBeansImpl(resultHandler); @@ -241,6 +243,7 @@ public abstract class AbstractPatchDAOImpl implements PatchDAO, BatchingDAO protected abstract void getOldAttrPropertyBackedBeansImpl(ResultHandler resultHandler); // note: caller's row handler is expected to migrate the attrs + @Override public void migrateOldAttrChainingURS(ResultHandler resultHandler) { getOldAttrChainingURSImpl(resultHandler); @@ -248,18 +251,11 @@ public abstract class AbstractPatchDAOImpl implements PatchDAO, BatchingDAO protected abstract void getOldAttrChainingURSImpl(ResultHandler resultHandler); + @Override public List getOldAttrCustomNames() { return getOldAttrCustomNamesImpl(); } protected abstract List getOldAttrCustomNamesImpl(); - - public void deleteAllOldAttrs() - { - deleteAllOldAttrsImpl(); - } - - protected abstract void deleteAllOldAttrsImpl(); - } diff --git a/source/java/org/alfresco/repo/domain/patch/PatchDAO.java b/source/java/org/alfresco/repo/domain/patch/PatchDAO.java index ac4860630c..bd0a7c973c 100644 --- a/source/java/org/alfresco/repo/domain/patch/PatchDAO.java +++ b/source/java/org/alfresco/repo/domain/patch/PatchDAO.java @@ -203,16 +203,16 @@ public interface PatchDAO */ public void migrateOldAttrChainingURS(ResultHandler resultHandler); + /** + * Drop old attribute alf_*attribute* tables + */ + public void migrateOldAttrDropTables(); + /** * Get custom global attribute names (if any) */ public List getOldAttrCustomNames(); - /** - * Delete all old attributes (from alf_*attribute* tables) - */ - public void deleteAllOldAttrs(); - /** * Get shared acls with inheritance issues * @return diff --git a/source/java/org/alfresco/repo/domain/patch/ibatis/AppliedPatchDAOImpl.java b/source/java/org/alfresco/repo/domain/patch/ibatis/AppliedPatchDAOImpl.java index 82857927dd..ca7462fef1 100644 --- a/source/java/org/alfresco/repo/domain/patch/ibatis/AppliedPatchDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/patch/ibatis/AppliedPatchDAOImpl.java @@ -73,37 +73,4 @@ public class AppliedPatchDAOImpl extends AbstractAppliedPatchDAOImpl { return (List) template.selectList(SELECT_ALL_APPLIED_PATCH); } - -// -// @Override -// protected EncodingEntity getEncodingEntity(Long id) -// { -// EncodingEntity encodingEntity = new EncodingEntity(); -// encodingEntity.setId(id); -// encodingEntity = (EncodingEntity) template.queryForObject(SELECT_ENCODING_BY_ID, encodingEntity); -// // Done -// return encodingEntity; -// } -// -// @Override -// protected EncodingEntity getEncodingEntity(String encoding) -// { -// EncodingEntity encodingEntity = new EncodingEntity(); -// encodingEntity.setEncoding(encoding == null ? null : encoding.toLowerCase()); -// encodingEntity = (EncodingEntity) template.queryForObject(SELECT_ENCODING_BY_KEY, encodingEntity); -// // Could be null -// return encodingEntity; -// } -// -// @Override -// protected EncodingEntity createEncodingEntity(String encoding) -// { -// EncodingEntity encodingEntity = new EncodingEntity(); -// encodingEntity.setVersion(MimetypeEntity.CONST_LONG_ZERO); -// encodingEntity.setEncoding(encoding == null ? null : encoding.toLowerCase()); -// Long id = (Long) template.insert(INSERT_ENCODING, encodingEntity); -// encodingEntity.setId(id); -// // Done -// return encodingEntity; -// } } diff --git a/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java b/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java index 1203420323..e113085e6b 100644 --- a/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java @@ -89,11 +89,12 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl private static final String SELECT_OLD_ATTR_CHAINING_URS = "alfresco.patch.select_oldAttrChainingURS"; private static final String SELECT_OLD_ATTR_CUSTOM_NAMES = "alfresco.patch.select_oldAttrCustomNames"; - private static final String DELETE_OLD_ATTR_LIST = "alfresco.patch.delete_oldAttrAlfListAttributeEntries"; - private static final String DELETE_OLD_ATTR_MAP = "alfresco.patch.delete_oldAttrAlfMapAttributeEntries"; - private static final String DELETE_OLD_ATTR_GLOBAL = "alfresco.patch.delete_oldAttrAlfGlobalAttributes"; - private static final String DELETE_OLD_ATTR = "alfresco.patch.delete_oldAttrAlfAttributes"; - + private static final String DROP_OLD_ATTR_LIST = "alfresco.patch.drop_oldAttrAlfListAttributeEntries"; + private static final String DROP_OLD_ATTR_MAP = "alfresco.patch.drop_oldAttrAlfMapAttributeEntries"; + private static final String DROP_OLD_ATTR_GLOBAL = "alfresco.patch.drop_oldAttrAlfGlobalAttributes"; + private static final String DROP_OLD_ATTR = "alfresco.patch.drop_oldAttrAlfAttributes"; + private static final String DROP_OLD_ATTR_SEQ = "alfresco.patch.drop_oldAttrAlfAttributes_seq"; + private static final String SELECT_ACLS_THAT_INHERIT_FROM_NON_PRIMARY_PARENT = "alfresco.patch.select_aclsThatInheritFromNonPrimaryParent"; private static final String SELECT_ACLS_THAT_INHERIT_WITH_INHERITANCE_UNSET = "alfresco.patch.select_aclsThatInheritWithInheritanceUnset"; private static final String SELECT_DEFINING_ACLS_THAT_DO_NOT_INHERIT_CORRECTLY_FROM_THE_PRIMARY_PARENT = "alfresco.patch.select_definingAclsThatDoNotInheritCorrectlyFromThePrimaryParent"; @@ -102,7 +103,7 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl private LocaleDAO localeDAO; - private SqlSessionTemplate template; + protected SqlSessionTemplate template; public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { @@ -538,23 +539,46 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl } @Override - protected void deleteAllOldAttrsImpl() + public void migrateOldAttrDropTables() { - int deleted = 0; - - deleted = template.delete(DELETE_OLD_ATTR_LIST); - logger.info("Deleted "+deleted+" rows from alf_list_attribute_entries"); - - deleted = template.delete(DELETE_OLD_ATTR_MAP); - logger.info("Deleted "+deleted+" rows from alf_map_attribute_entries"); - - deleted = template.delete(DELETE_OLD_ATTR_GLOBAL); - logger.info("Deleted "+deleted+" rows from alf_global_attributes"); - - deleted = template.delete(DELETE_OLD_ATTR); - logger.info("Deleted "+deleted+" rows from alf_attributes"); + template.update(DROP_OLD_ATTR_LIST); + template.update(DROP_OLD_ATTR_MAP); + template.update(DROP_OLD_ATTR_GLOBAL); + template.update(DROP_OLD_ATTR); } + /** + * PostgreSQL-specific DAO + * + * @author Derek Hulley + * @since 4.0 + */ + public static class PostgreSQL extends PatchDAOImpl + { + @Override + public void migrateOldAttrDropTables() + { + super.migrateOldAttrDropTables(); + template.update(DROP_OLD_ATTR_SEQ); + } + } + + /** + * Oracle-specific DAO + * + * @author Derek Hulley + * @since 4.0 + */ + public static class Oracle extends PatchDAOImpl + { + @Override + public void migrateOldAttrDropTables() + { + super.migrateOldAttrDropTables(); + template.update(DROP_OLD_ATTR_SEQ); + } + } + @SuppressWarnings("unchecked") @Override public List> getAclsThatInheritFromNonPrimaryParent() diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java index 7136a99c8a..8e2277e4ea 100644 --- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java @@ -795,4 +795,10 @@ public abstract class AbstractNodeServiceImpl implements NodeService { throw new UnsupportedOperationException(); } + + @Override + public final boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef) + { + return removeSecondaryChildAssociation(childAssocRef); + } } diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 8f1e25832e..74fd3695be 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -1254,7 +1254,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } } - public boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef) + @Override + public boolean removeSecondaryChildAssociation(ChildAssociationRef childAssocRef) { Long parentNodeId = getNodePairNotNull(childAssocRef.getParentRef()).getFirst(); Long childNodeId = getNodePairNotNull(childAssocRef.getChildRef()).getFirst(); diff --git a/source/java/org/alfresco/repo/node/locator/AbstractNodeLocator.java b/source/java/org/alfresco/repo/node/locator/AbstractNodeLocator.java new file mode 100644 index 0000000000..db3775dae9 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/AbstractNodeLocator.java @@ -0,0 +1,51 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeLocator; +import org.alfresco.service.cmr.repository.NodeLocatorService; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public abstract class AbstractNodeLocator implements NodeLocator +{ + + public void setNodeLocatorService(NodeLocatorService nodeLocatorService) + { + nodeLocatorService.register(getName(), this); + } + + /** + * {@inheritDoc} + */ + public List getParameterDefinitions() + { + return Collections.emptyList(); + } + + public abstract String getName(); +} diff --git a/source/java/org/alfresco/repo/node/locator/AncestorNodeLocator.java b/source/java/org/alfresco/repo/node/locator/AncestorNodeLocator.java new file mode 100644 index 0000000000..680e8a3cc7 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/AncestorNodeLocator.java @@ -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 . + */ + +package org.alfresco.repo.node.locator; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class AncestorNodeLocator extends AbstractNodeLocator +{ + public static final String NAME = "AncestorNodeLocator"; + public static final String TYPE_KEY = "type"; + public static final String ASPECT_KEY = "aspect"; + + private NamespaceService namespaceService; + private NodeService nodeService; + + /** + * {@inheritDoc} + */ + @Override + public NodeRef getNode(NodeRef source, Map params) + { + QName type = getQNameParam(TYPE_KEY, params); + QName aspect = getQNameParam(ASPECT_KEY, params); + NodeRef child = source; + while(true) + { + ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(child); + if(parentAssoc == null) + { + break; // No matching ancestor found. + } + NodeRef parent = parentAssoc.getParentRef(); + if(parent == null) + { + break; // No matching ancestor found. + } + if(typeMatches(type, parent) && aspectMatches(aspect, parent)) + { + return parent; // Matching ancestor was found. + } + child = parent; + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public List getParameterDefinitions() + { + List paramDefs = new ArrayList(2); + paramDefs.add(new ParameterDefinitionImpl(TYPE_KEY, DataTypeDefinition.QNAME, false, "Type")); + paramDefs.add(new ParameterDefinitionImpl(ASPECT_KEY, DataTypeDefinition.QNAME, false, "Aspect")); + return paramDefs; + } + + private boolean typeMatches(QName type, NodeRef parent) + { + return type==null || type.equals(nodeService.getType(parent)); + } + + private boolean aspectMatches(QName aspect, NodeRef parent) + { + return aspect==null || nodeService.getAspects(parent).contains(aspect); + } + + private QName getQNameParam(String key, Map params) + { + String value = (String) params.get(key); + if(value!=null) + { + return QName.createQName(value, namespaceService); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + + /** + * @param nodeService the nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param namespaceService the namespaceService to set + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + +} diff --git a/source/java/org/alfresco/repo/node/locator/CompanyHomeNodeLocator.java b/source/java/org/alfresco/repo/node/locator/CompanyHomeNodeLocator.java new file mode 100644 index 0000000000..9833a68095 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/CompanyHomeNodeLocator.java @@ -0,0 +1,67 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.model.Repository; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Locates the Company Home {@link NodeRef}. + * + * @author Nick Smith + * @since 4.0 + * + */ +public class CompanyHomeNodeLocator extends AbstractNodeLocator +{ + public static final String NAME = "CompanyHomeNodeLocator"; + + private Repository repoHelper; + + /** + * {@inheritDoc} + */ + public NodeRef getNode(NodeRef source, Map params) + { + return repoHelper.getCompanyHome(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + + /** + * @param repoHelper the repoHelper to set + */ + public void setRepositoryHelper(Repository repoHelper) + { + this.repoHelper = repoHelper; + } + +} diff --git a/source/java/org/alfresco/repo/node/locator/DocLibNodeLocator.java b/source/java/org/alfresco/repo/node/locator/DocLibNodeLocator.java new file mode 100644 index 0000000000..312774b9be --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/DocLibNodeLocator.java @@ -0,0 +1,98 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.model.Repository; +import org.alfresco.service.cmr.repository.NodeLocator; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; + +/** + * This {@link NodeLocator} identifies the site in which the source node resides and returns the Document Library container for that site. + * If no site can be found or the site does not have a Document Library then the Company Home is returned. + * @author Nick Smith + * @since 4.0 + * + */ +public class DocLibNodeLocator extends AbstractNodeLocator +{ + public static final String NAME = "DocumentLibraryNodeLocator"; + + private SiteService siteService; + private Repository repositoryHelper; + + /** + * Finds the site in which the source {@link NodeRef} resides and returns the Document Library container for that site. + * If no site can be found or the site does not have a Document Library then the Company Home is returned. + * + * @param sourceNode the starting point for locating the site Document Library. + * @param params Not used. + * @return the Document Library or the Company Home. + */ + @Override + public NodeRef getNode(NodeRef source, Map params) + { + NodeRef docLib = null; + if(source != null) + { + SiteInfo siteInfo = siteService.getSite(source); + if(siteInfo != null) + { + String siteName = siteInfo.getShortName(); + String containerId = SiteService.DOCUMENT_LIBRARY; + docLib = siteService.getContainer(siteName, containerId); + } + } + if(docLib == null) + { + docLib = repositoryHelper.getCompanyHome(); + } + return docLib; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + + /** + * @param siteService the siteService to set + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @param repositoryHelper the repositoryHelper to set + */ + public void setRepositoryHelper(Repository repositoryHelper) + { + this.repositoryHelper = repositoryHelper; + } +} diff --git a/source/java/org/alfresco/repo/node/locator/NodeLocatorServiceImpl.java b/source/java/org/alfresco/repo/node/locator/NodeLocatorServiceImpl.java new file mode 100644 index 0000000000..157682e778 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/NodeLocatorServiceImpl.java @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeLocator; +import org.alfresco.service.cmr.repository.NodeLocatorService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.ParameterCheck; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class NodeLocatorServiceImpl implements NodeLocatorService +{ + private final Map locators = new HashMap(); + + /** + * {@inheritDoc} + */ + @Override + public NodeRef getNode(String locatorName, NodeRef source, Map params) + { + NodeLocator locator = locators.get(locatorName); + if(locator == null) + { + String msg = "No NodeLocator is registered with name " +locatorName; + throw new IllegalArgumentException(msg); + } + return locator.getNode(source, params); + } + + /** + * {@inheritDoc} + */ + @Override + public void register(String locatorName, NodeLocator locator) + { + ParameterCheck.mandatory("locatorName", locatorName); + ParameterCheck.mandatory("locator", locator); + if(locators.containsKey(locatorName)) + { + String msg = "Locator with name: " +locatorName + " is already registered!"; + throw new IllegalArgumentException(msg); + } + locators.put(locatorName, locator); + } + +} diff --git a/source/java/org/alfresco/repo/node/locator/NodeLocatorServiceImplTest.java b/source/java/org/alfresco/repo/node/locator/NodeLocatorServiceImplTest.java new file mode 100644 index 0000000000..efa3ff4241 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/NodeLocatorServiceImplTest.java @@ -0,0 +1,102 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + +import static junit.framework.Assert.*; +import static org.mockito.Mockito.when; + +import javax.annotation.Resource; + +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.site.SiteServiceInternal; +import org.alfresco.service.cmr.repository.NodeLocatorService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations={"classpath:alfresco/node-locator-context.xml", "classpath:test-nodeLocatorServiceImpl-context.xml"}) +public class NodeLocatorServiceImplTest +{ + private static final NodeRef companyHome = new NodeRef("alfresco://company/home"); + private static final NodeRef sitesHome = new NodeRef("alfresco://sites/home"); + + @Resource + private NodeLocatorService nodeLocatorService; + + @Autowired + private Repository repositoryHelper; + + @Autowired + private SiteServiceInternal siteService; + + @Test + public void testUnknownNodeLocator() throws Exception + { + try + { + nodeLocatorService.getNode(null, null, null); + fail("An exception should have been thrown!"); + } + catch(IllegalArgumentException e) + { + //NOOP + } + try + { + nodeLocatorService.getNode("some unknown name", null, null); + fail("An exception should have been thrown!"); + } + catch(IllegalArgumentException e) + { + //NOOP + } + } + + @Test + public void testCompanyHomeNodeLocator() throws Exception + { + NodeRef result = nodeLocatorService.getNode(CompanyHomeNodeLocator.NAME, null, null); + assertEquals(companyHome, result); + } + + @Test + public void testSitesHomeNodeLocator() throws Exception + { + NodeRef result = nodeLocatorService.getNode(SitesHomeNodeLocator.NAME, null, null); + assertEquals(sitesHome, result); + } + + @Before + public void setUpClass() + { + when(repositoryHelper.getCompanyHome()).thenReturn(companyHome); + when(siteService.getSiteRoot()).thenReturn(sitesHome); + } +} diff --git a/source/java/org/alfresco/service/cmr/rendition/NodeLocator.java b/source/java/org/alfresco/repo/node/locator/SelfNodeLocator.java similarity index 58% rename from source/java/org/alfresco/service/cmr/rendition/NodeLocator.java rename to source/java/org/alfresco/repo/node/locator/SelfNodeLocator.java index 90d59ab981..116d9647ef 100644 --- a/source/java/org/alfresco/service/cmr/rendition/NodeLocator.java +++ b/source/java/org/alfresco/repo/node/locator/SelfNodeLocator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco * @@ -17,7 +17,7 @@ * along with Alfresco. If not, see . */ -package org.alfresco.service.cmr.rendition; +package org.alfresco.repo.node.locator; import java.io.Serializable; import java.util.Map; @@ -25,19 +25,29 @@ import java.util.Map; import org.alfresco.service.cmr.repository.NodeRef; /** - * This interface defines a strategy object used for finding a {@link NodeRef}. - * * @author Nick Smith + * @since 4.0 + * */ -public interface NodeLocator +public class SelfNodeLocator extends AbstractNodeLocator { + public static final String NAME = "SelfNodeLocator"; /** - * Finds a {@link NodeRef} given a starting {@link NodeRef} and a - * {@link Map} of parameters. - * - * @param sourceNode the starting point for locating a new node. - * @param params a {@link Map} of parameters. - * @return the {@link NodeRef}. - */ - NodeRef getNode(NodeRef sourceNode, Map params); + * {@inheritDoc} + */ + @Override + public NodeRef getNode(NodeRef source, Map params) + { + return source; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + } diff --git a/source/java/org/alfresco/repo/node/locator/SitesHomeNodeLocator.java b/source/java/org/alfresco/repo/node/locator/SitesHomeNodeLocator.java new file mode 100644 index 0000000000..af2b563b2d --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/SitesHomeNodeLocator.java @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.site.SiteServiceInternal; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class SitesHomeNodeLocator extends AbstractNodeLocator +{ + public static final String NAME = "SitesHomeNodeLocator"; + + SiteServiceInternal siteService; + + /** + * {@inheritDoc} + */ + @Override + public NodeRef getNode(NodeRef source, Map params) + { + return siteService.getSiteRoot(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + + /** + * @param siteService the siteService to set + */ + public void setSiteService(SiteServiceInternal siteService) + { + this.siteService = siteService; + } + +} diff --git a/source/java/org/alfresco/repo/node/locator/UserHomeNodeLocator.java b/source/java/org/alfresco/repo/node/locator/UserHomeNodeLocator.java new file mode 100644 index 0000000000..75c7612898 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/UserHomeNodeLocator.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.model.Repository; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class UserHomeNodeLocator extends AbstractNodeLocator +{ + public static final String NAME = "UserHomeNodeLocator"; + private Repository repositoryHelper; + + /** + * {@inheritDoc} + */ + @Override + public NodeRef getNode(NodeRef source, Map params) + { + NodeRef person = repositoryHelper.getPerson(); + if(person != null) + { + return repositoryHelper.getUserHome(person); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + + /** + * @param repositoryHelper the repositoryHelper to set + */ + public void setRepositoryHelper(Repository repositoryHelper) + { + this.repositoryHelper = repositoryHelper; + } +} diff --git a/source/java/org/alfresco/repo/node/locator/XPathNodeLocator.java b/source/java/org/alfresco/repo/node/locator/XPathNodeLocator.java new file mode 100644 index 0000000000..f8c7aa8d82 --- /dev/null +++ b/source/java/org/alfresco/repo/node/locator/XPathNodeLocator.java @@ -0,0 +1,129 @@ +/* + * 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 . + */ + +package org.alfresco.repo.node.locator; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.util.ParameterCheck; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class XPathNodeLocator extends AbstractNodeLocator +{ + public static final String NAME = "XPathNodeLocator"; + public static final String QUERY_KEY = "query"; + public static final String STORE_TYPE_KEY = "store_type"; + public static final String STORE_ID_KEY = "store_id"; + + private SearchService searchService; + private StoreRef defaultStore; + + /** + * {@inheritDoc} + */ + @Override + public NodeRef getNode(NodeRef source, Map params) + { + String query = (String) params.get(QUERY_KEY); + ParameterCheck.mandatoryString("query", query); + StoreRef store = null; + if(source!=null) + { + store = source.getStoreRef(); + } + else + { + String storeType = (String) params.get(STORE_TYPE_KEY); + String storeId = (String) params.get(STORE_ID_KEY); + if(storeType !=null && storeId != null) + { + store = new StoreRef(storeType, storeId); + } + else store = defaultStore; + } + try + { + ResultSet results = searchService.query(store, SearchService.LANGUAGE_XPATH, query); + List nodes = results.getNodeRefs(); + if (nodes.size() > 0) + { + return nodes.get(0); + } + } + catch(Exception e) + { + String msg = "Error while searching XPath. StoreRef: " + store + " Query: " + query; + throw new AlfrescoRuntimeException(msg, e); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public List getParameterDefinitions() + { + List paramDefs = new ArrayList(2); + paramDefs.add(new ParameterDefinitionImpl(QUERY_KEY, DataTypeDefinition.TEXT, true, "Query")); + paramDefs.add(new ParameterDefinitionImpl(STORE_TYPE_KEY, DataTypeDefinition.TEXT, false, "Store Type")); + paramDefs.add(new ParameterDefinitionImpl(STORE_ID_KEY, DataTypeDefinition.TEXT, false, "Store Id")); + return paramDefs; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return NAME; + } + + /** + * @param searchService the searchService to set + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + /** + * @param defaultStore the defaultStore to set + */ + public void setDefaultStore(String defaultStoreStr) + { + this.defaultStore = new StoreRef(defaultStoreStr); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java index 99672429e9..0ac1fbdea7 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java @@ -36,6 +36,7 @@ import org.alfresco.model.RenditionModel; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.node.locator.SelfNodeLocator; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl; import org.alfresco.repo.rendition.RenditionDefinitionImpl; @@ -47,7 +48,6 @@ import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.rendition.NodeLocator; import org.alfresco.service.cmr.rendition.RenderCallback; import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionService; @@ -56,6 +56,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeLocator; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceException; @@ -189,14 +190,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase /** * Default {@link NodeLocator} simply returns the source node. */ - private final static NodeLocator defaultNodeLocator = new NodeLocator() - { - @Override - public NodeRef getNode(NodeRef sourceNode, Map params) - { - return sourceNode; - } - }; + private final static NodeLocator defaultNodeLocator = new SelfNodeLocator(); /* * Injected beans diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 320d637757..0ebeb525f8 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco * @@ -88,7 +88,6 @@ import org.alfresco.util.EqualsHelper; import org.alfresco.util.GUID; import org.alfresco.util.ModelUtil; import org.alfresco.util.PropertyCheck; -import org.alfresco.util.UrlUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; @@ -130,7 +129,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per private PermissionsManager permissionsManager; private RepoAdminService repoAdminService; private ServiceRegistry serviceRegistry; - + private boolean createMissingPeople; private static Set mutableProperties; private String defaultHomeFolderProvider; @@ -182,7 +181,6 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per PropertyCheck.mandatory(this, "storeUrl", storeRef); PropertyCheck.mandatory(this, "transactionService", transactionService); PropertyCheck.mandatory(this, "nodeService", nodeService); - PropertyCheck.mandatory(this, "searchService", searchService); PropertyCheck.mandatory(this, "permissionServiceSPI", permissionServiceSPI); PropertyCheck.mandatory(this, "authorityService", authorityService); PropertyCheck.mandatory(this, "authenticationService", authenticationService); diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 01827e5e6b..6f01f89afc 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco @@ -14,1095 +14,1092 @@ * 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 . - */ -package org.alfresco.repo.site; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.StringTokenizer; -import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.activities.ActivityType; -import org.alfresco.repo.admin.SysAdminParams; + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.site; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.StringTokenizer; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.activities.ActivityType; +import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.policy.BehaviourFilter; -import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; -import org.alfresco.repo.security.authentication.AuthenticationContext; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.tenant.TenantAdminService; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.activities.ActivityService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.model.FileNotFoundException; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.ResultSetRow; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AccessPermission; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.site.SiteInfo; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.cmr.site.SiteVisibility; -import org.alfresco.service.cmr.tagging.TaggingService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.util.PropertyCheck; -import org.alfresco.util.PropertyMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.extensions.surf.util.ParameterCheck; - -/** - * Site Service Implementation. Also bootstraps the site AVM and DM stores. - * - * @author Roy Wetherall - */ -public class SiteServiceImpl implements SiteService, SiteModel -{ - /** Logger */ - private static Log logger = LogFactory.getLog(SiteServiceImpl.class); - - /** The DM store where site's are kept */ - public static final StoreRef SITE_STORE = new StoreRef("workspace://SpacesStore"); - - /** Activity tool */ - private static final String ACTIVITY_TOOL = "siteService"; - - private static final String SITE_PREFIX = "site_"; - private static final String GROUP_SITE_PREFIX = PermissionService.GROUP_PREFIX + SITE_PREFIX; - private static final int GROUP_PREFIX_LENGTH = PermissionService.GROUP_PREFIX.length(); - private static final int GROUP_SITE_PREFIX_LENGTH = GROUP_SITE_PREFIX.length(); - - /** Site home ref cache (Tennant aware) */ - private Map siteHomeRefs = new ConcurrentHashMap(4); - - /** Site node ref cache (Tennant aware) */ - private Map siteNodeRefs = new ConcurrentHashMap(256); - - private String sitesXPath; - - /** Messages */ - private static final String MSG_UNABLE_TO_CREATE = "site_service.unable_to_create"; - private static final String MSG_VISIBILITY_GROUP_MISSING = "site_service.visibility_group_missing"; - private static final String MSG_CAN_NOT_UPDATE = "site_service.can_not_update"; - private static final String MSG_CAN_NOT_DELETE = "site_service.can_not_delete"; - private static final String MSG_SITE_NO_EXIST = "site_service.site_no_exist"; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; +import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantAdminService; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.PropertyMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Site Service Implementation. Also bootstraps the site AVM and DM stores. + * + * @author Roy Wetherall + */ +public class SiteServiceImpl implements SiteServiceInternal, SiteModel +{ + /** Logger */ + private static Log logger = LogFactory.getLog(SiteServiceImpl.class); + + /** The DM store where site's are kept */ + public static final StoreRef SITE_STORE = new StoreRef("workspace://SpacesStore"); + + /** Activity tool */ + private static final String ACTIVITY_TOOL = "siteService"; + + private static final String SITE_PREFIX = "site_"; + private static final String GROUP_SITE_PREFIX = PermissionService.GROUP_PREFIX + SITE_PREFIX; + private static final int GROUP_PREFIX_LENGTH = PermissionService.GROUP_PREFIX.length(); + private static final int GROUP_SITE_PREFIX_LENGTH = GROUP_SITE_PREFIX.length(); + + /** Site home ref cache (Tennant aware) */ + private Map siteHomeRefs = new ConcurrentHashMap(4); + + /** Site node ref cache (Tennant aware) */ + private Map siteNodeRefs = new ConcurrentHashMap(256); + + private String sitesXPath; + + /** Messages */ + private static final String MSG_UNABLE_TO_CREATE = "site_service.unable_to_create"; + private static final String MSG_VISIBILITY_GROUP_MISSING = "site_service.visibility_group_missing"; + private static final String MSG_CAN_NOT_UPDATE = "site_service.can_not_update"; + private static final String MSG_CAN_NOT_DELETE = "site_service.can_not_delete"; + private static final String MSG_SITE_NO_EXIST = "site_service.site_no_exist"; private static final String MSG_CAN_NOT_REMOVE_MSHIP = "site_service.can_not_remove_membership"; - private static final String MSG_DO_NOT_CHANGE_MGR = "site_service.do_not_change_manager"; + private static final String MSG_DO_NOT_CHANGE_MGR = "site_service.do_not_change_manager"; private static final String MSG_CAN_NOT_CHANGE_MSHIP="site_service.can_not_change_membership"; - private static final String MSG_SITE_CONTAINER_NOT_FOLDER = "site_service.site_container_not_folder"; - - /* Services */ - private NodeService nodeService; - private FileFolderService fileFolderService; - private SearchService searchService; - private NamespaceService namespaceService; - private PermissionService permissionService; - private ActivityService activityService; - private PersonService personService; - private AuthenticationContext authenticationContext; - private TaggingService taggingService; - private AuthorityService authorityService; - private DictionaryService dictionaryService; - private TenantService tenantService; - private TenantAdminService tenantAdminService; - private RetryingTransactionHelper retryingTransactionHelper; - private Comparator roleComparator; - private SysAdminParams sysAdminParams; + private static final String MSG_SITE_CONTAINER_NOT_FOLDER = "site_service.site_container_not_folder"; + + /* Services */ + private NodeService nodeService; + private FileFolderService fileFolderService; + private SearchService searchService; + private NamespaceService namespaceService; + private PermissionService permissionService; + private ActivityService activityService; + private PersonService personService; + private AuthenticationContext authenticationContext; + private TaggingService taggingService; + private AuthorityService authorityService; + private DictionaryService dictionaryService; + private TenantService tenantService; + private TenantAdminService tenantAdminService; + private RetryingTransactionHelper retryingTransactionHelper; + private Comparator roleComparator; + private SysAdminParams sysAdminParams; private BehaviourFilter behaviourFilter; private SitesPermissionCleaner sitesPermissionsCleaner; - - - /** - * Set the path to the location of the sites root folder. For example: - *

-     * ./app:company_home/st:sites
-     * 
- * @param sitesXPath a valid XPath - */ - public void setSitesXPath(String sitesXPath) - { - this.sitesXPath = sitesXPath; - } - - /** - * Set node service - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - /** - * Set file folder service - */ - public void setFileFolderService(FileFolderService fileFolderService) - { - this.fileFolderService = fileFolderService; - } - - /** - * Set search service - */ - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - - /** - * Set Namespace service - */ - public void setNamespaceService(NamespaceService namespaceService) - { - this.namespaceService = namespaceService; - } - - /** - * Set permission service - */ - public void setPermissionService(PermissionService permissionService) - { - this.permissionService = permissionService; - } - - /** - * Set activity service - */ - public void setActivityService(ActivityService activityService) - { - this.activityService = activityService; - } - - /** - * Set person service - */ - public void setPersonService(PersonService personService) - { - this.personService = personService; - } - - /** - * Set authentication component - */ - public void setAuthenticationContext( - AuthenticationContext authenticationContext) - { - this.authenticationContext = authenticationContext; - } - - /** - * Set the tagging service - */ - public void setTaggingService(TaggingService taggingService) - { - this.taggingService = taggingService; - } - - /** - * Set the authority service - */ - public void setAuthorityService(AuthorityService authorityService) - { - this.authorityService = authorityService; - } - - /** - * Set the dictionary service - * - * @param dictionaryService dictionary service - */ - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - /** - * Set the tenant service - * - * @param tenantService tenant service - */ - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - /** - * Sets the tenant admin service - */ - public void setTenantAdminService(TenantAdminService tenantAdminService) - { - this.tenantAdminService = tenantAdminService; - } - - /** - * Sets helper that provides transaction callbacks - */ - public void setTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) - { - this.retryingTransactionHelper = retryingTransactionHelper; - } - - public void setRoleComparator(Comparator roleComparator) - { - this.roleComparator = roleComparator; - } - - public void setSysAdminParams(SysAdminParams sysAdminParams) - { - this.sysAdminParams = sysAdminParams; - } + + + /** + * Set the path to the location of the sites root folder. For example: + *
+     * ./app:company_home/st:sites
+     * 
+ * @param sitesXPath a valid XPath + */ + public void setSitesXPath(String sitesXPath) + { + this.sitesXPath = sitesXPath; + } + + /** + * Set node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set file folder service + */ + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + /** + * Set search service + */ + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + /** + * Set Namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * Set permission service + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * Set activity service + */ + public void setActivityService(ActivityService activityService) + { + this.activityService = activityService; + } + + /** + * Set person service + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * Set authentication component + */ + public void setAuthenticationContext( + AuthenticationContext authenticationContext) + { + this.authenticationContext = authenticationContext; + } + + /** + * Set the tagging service + */ + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + /** + * Set the authority service + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * Set the dictionary service + * + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the tenant service + * + * @param tenantService tenant service + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Sets the tenant admin service + */ + public void setTenantAdminService(TenantAdminService tenantAdminService) + { + this.tenantAdminService = tenantAdminService; + } + + /** + * Sets helper that provides transaction callbacks + */ + public void setTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) + { + this.retryingTransactionHelper = retryingTransactionHelper; + } + + public void setRoleComparator(Comparator roleComparator) + { + this.roleComparator = roleComparator; + } + + public void setSysAdminParams(SysAdminParams sysAdminParams) + { + this.sysAdminParams = sysAdminParams; + } public void setBehaviourFilter(BehaviourFilter behaviourFilter) { this.behaviourFilter = behaviourFilter; } - + public void setSitesPermissionsCleaner(SitesPermissionCleaner sitesPermissionsCleaner) { this.sitesPermissionsCleaner = sitesPermissionsCleaner; } - public Comparator getRoleComparator() - { - return roleComparator; - } - - /** - * Checks that all necessary properties and services have been provided. - */ - public void init() - { - PropertyCheck.mandatory(this, "nodeService", nodeService); - PropertyCheck.mandatory(this, "fileFolderService", fileFolderService); - PropertyCheck.mandatory(this, "searchService", searchService); - PropertyCheck.mandatory(this, "namespaceService", namespaceService); - PropertyCheck.mandatory(this, "permissionService", permissionService); - PropertyCheck.mandatory(this, "authenticationContext", authenticationContext); - PropertyCheck.mandatory(this, "personService", personService); - PropertyCheck.mandatory(this, "activityService", activityService); - PropertyCheck.mandatory(this, "taggingService", taggingService); - PropertyCheck.mandatory(this, "authorityService", authorityService); - PropertyCheck.mandatory(this, "sitesXPath", sitesXPath); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.site.SiteService#hasCreateSitePermissions() - */ - public boolean hasCreateSitePermissions() - { - final NodeRef siteRoot = getSiteRoot(); - if (siteRoot == null) - { - throw new SiteServiceException("No root sites folder exists"); - } - boolean result = permissionService.hasPermission(siteRoot, PermissionService.CONTRIBUTOR).equals(AccessStatus.ALLOWED); - return result; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#createSite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) - */ - public SiteInfo createSite(final String sitePreset, - String passedShortName, - final String title, - final String description, - final boolean isPublic) - { - // Determine the site visibility - SiteVisibility visibility = SiteVisibility.PRIVATE; - if (isPublic == true) - { - visibility = SiteVisibility.PUBLIC; - } - - // Create the site - return createSite(sitePreset, passedShortName, title, description, visibility); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#createSite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) - */ - public SiteInfo createSite(final String sitePreset, - String passedShortName, - final String title, - final String description, - final SiteVisibility visibility) - { - // Remove spaces from shortName - final String shortName = passedShortName.replaceAll(" ", ""); - - /** - * Check that the site does not already exist - */ - // Check to see if we already have a site of this name - NodeRef existingSite = getSiteNodeRef(shortName); - if (existingSite != null) - { - // Throw an exception since we have a duplicate site name - throw new SiteServiceException(MSG_UNABLE_TO_CREATE, new Object[]{shortName}); - } - - // Get the site parent node reference - NodeRef siteParent = getSiteParent(shortName); - if (siteParent == null) - { - throw new SiteServiceException("No root sites folder exists"); - } - - // Create the site node - PropertyMap properties = new PropertyMap(4); - properties.put(ContentModel.PROP_NAME, shortName); - properties.put(SiteModel.PROP_SITE_PRESET, sitePreset); - properties.put(SiteModel.PROP_SITE_VISIBILITY, visibility.toString()); - properties.put(ContentModel.PROP_TITLE, title); - properties.put(ContentModel.PROP_DESCRIPTION, description); - - final NodeRef siteNodeRef = this.nodeService.createNode( - siteParent, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - shortName), SiteModel.TYPE_SITE, properties) - .getChildRef(); - - // Make the new site a tag scope - this.taggingService.addTagScope(siteNodeRef); - - // Clear the sites inherited permissions - this.permissionService.setInheritParentPermissions(siteNodeRef, false); - - // Get the current user - final String currentUser = authenticationContext.getCurrentUserName(); - - // Create the relevant groups and assign permissions - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public String doWork() throws Exception - { - Set shareZones = new HashSet(2, 1.0f); - shareZones.add(AuthorityService.ZONE_APP_SHARE); - shareZones.add(AuthorityService.ZONE_AUTH_ALFRESCO); - - // Create the site's groups - String siteGroup = authorityService - .createAuthority(AuthorityType.GROUP, getSiteGroup(shortName, false), shortName, shareZones); - QName siteType = nodeService.getType(siteNodeRef); - Set permissions = permissionService.getSettablePermissions(siteType); - for (String permission : permissions) - { - // Create a group for the permission - String permissionGroup = authorityService.createAuthority(AuthorityType.GROUP, getSiteRoleGroup( - shortName, permission, false), shortName, shareZones); - authorityService.addAuthority(siteGroup, permissionGroup); - - // Assign the group the relevant permission on the site - permissionService.setPermission(siteNodeRef, permissionGroup, permission, true); - } - - // Set the memberships details - // - give all authorities site consumer if site is public - // - give all authorities read properties if site is moderated - // - give all authorities read permission on permissions so - // memberships can be calculated - // - add the current user to the site manager group - if (SiteVisibility.PUBLIC.equals(visibility) == true && - permissions.contains(SITE_CONSUMER)) - { - // From Alfresco 3.4 the 'site public' group is configurable. Out of the box it is - // GROUP_EVERYONE so unconfigured behaviour is unchanged. But from 3.4 admins - // can change the value of property site.public.group via JMX/properties files - // to be another group of their choosing. - // This then is the group that is given SiteConsumer access to newly created sites. - final String sitePublicGroup = sysAdminParams.getSitePublicGroup(); - - boolean groupExists = authorityService.authorityExists(sitePublicGroup); - if (!PermissionService.ALL_AUTHORITIES.equals(sitePublicGroup) && !groupExists) - { - // If the group does not exist, we cannot create the site. - throw new SiteServiceException(MSG_VISIBILITY_GROUP_MISSING, new Object[]{sitePublicGroup}); - } - - permissionService.setPermission(siteNodeRef, sitePublicGroup, SITE_CONSUMER, true); - } - else if (SiteVisibility.MODERATED.equals(visibility) == true && - permissions.contains(SITE_CONSUMER)) - { - // for moderated site EVERYONE has consumer access but site components do not. - permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); - } - permissionService.setPermission(siteNodeRef, - PermissionService.ALL_AUTHORITIES, - PermissionService.READ_PERMISSIONS, true); - authorityService.addAuthority(getSiteRoleGroup(shortName, - SiteModel.SITE_MANAGER, true), currentUser); - - // Return nothing - return null; - } - - }, AuthenticationUtil.getSystemUserName()); - - // Return created site information - Map customProperties = getSiteCustomProperties(siteNodeRef); - SiteInfo siteInfo = new SiteInfoImpl(sitePreset, shortName, title, description, visibility, customProperties, siteNodeRef); - return siteInfo; - } - - /** - * Gets a map containing the site's custom properties - * - * @return Map map containing the custom properties of the site - */ - private Map getSiteCustomProperties(Map properties) - { - Map customProperties = new HashMap(4); - - for (Map.Entry entry : properties.entrySet()) - { - if (entry.getKey().getNamespaceURI().equals(SITE_CUSTOM_PROPERTY_URL) == true) - { - customProperties.put(entry.getKey(), entry.getValue()); - } - } - - return customProperties; - } - - /** - * Gets a map containing the site's custom properties - * - * @return Map map containing the custom properties of the site - */ - private Map getSiteCustomProperties(NodeRef siteNodeRef) - { - Map customProperties = new HashMap(4); - Map properties = nodeService.getProperties(siteNodeRef); - - for (Map.Entry entry : properties.entrySet()) - { - if (entry.getKey().getNamespaceURI().equals(SITE_CUSTOM_PROPERTY_URL) == true) - { - customProperties.put(entry.getKey(), entry.getValue()); - } - } - - return customProperties; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSiteGroup(java.lang.String) - */ - public String getSiteGroup(String shortName) - { - return getSiteGroup(shortName, true); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSiteRoleGroup(java.lang.String, - * java.lang.String) - */ - public String getSiteRoleGroup(String shortName, String role) - { - return getSiteRoleGroup(shortName, role, true); - } - - /** - * Helper method to get the name of the site group - * - * @param shortName site short name - * @return String site group name - */ - public String getSiteGroup(String shortName, boolean withGroupPrefix) - { - StringBuffer sb = new StringBuffer(64); - if (withGroupPrefix == true) - { - sb.append(PermissionService.GROUP_PREFIX); - } - sb.append(SITE_PREFIX); - sb.append(shortName); - return sb.toString(); - } - - /** - * Helper method to get the name of the site permission group - * - * @param shortName site short name - * @param permission permission name - * @param withGroupPrefix - should the name have the GROUP_ prefix? - * @return String site permission group name - */ - public String getSiteRoleGroup(String shortName, String permission, boolean withGroupPrefix) - { - return getSiteGroup(shortName, withGroupPrefix) + '_' + permission; - } - - /** - * Gets a sites parent folder based on it's short name - * - * @param shortName site short name - * @return NodeRef the site's parent - */ - private NodeRef getSiteParent(String shortName) - { - // TODO: For now just return the site root, later we may build folder - // structure based on the shortname to spread the sites about - return getSiteRoot(); - } - - /** - * Get the node reference that is the site root - * - * @return NodeRef node reference - */ - private NodeRef getSiteRoot() - { - String tenantDomain = tenantAdminService.getCurrentUserDomain(); - NodeRef siteHomeRef = siteHomeRefs.get(tenantDomain); - if (siteHomeRef == null) - { - siteHomeRef = AuthenticationUtil.runAs(new RunAsWork() - { - public NodeRef doWork() throws Exception - { - return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() - { - public NodeRef execute() throws Exception - { - NodeRef result = null; - - // Get the root 'sites' folder - NodeRef rootNodeRef = nodeService.getRootNode(SITE_STORE); - List results = searchService.selectNodes( - rootNodeRef, - sitesXPath, - null, - namespaceService, - false, - SearchService.LANGUAGE_XPATH); - if (results.size() != 0) - { - result = results.get(0); - } - - return result; - } - }, true); - } - }, AuthenticationUtil.getSystemUserName()); - - // There may be domains with no sites (e.g. JSF-only clients). - if (siteHomeRef != null) - { - siteHomeRefs.put(tenantDomain, siteHomeRef); - } - } - return siteHomeRef; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String, java.lang.String) - */ - public List listSites(String nameFilter, String sitePresetFilter) - { - return listSites(nameFilter, sitePresetFilter, 0); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String, java.lang.String, int) - */ - public List listSites(String nameFilter, String sitePresetFilter, int size) - { - List result; - - NodeRef siteRoot = getSiteRoot(); - if (siteRoot == null) - { - result = Collections.emptyList(); - } - else - { - if (nameFilter != null && nameFilter.length() != 0 || sitePresetFilter != null && sitePresetFilter.length() > 0) - { - // get the sites that match the specified names - StringBuilder query = new StringBuilder(128); - query.append("+PARENT:\"").append(siteRoot.toString()) - .append("\" +("); - - if (nameFilter != null && nameFilter.length() > 0) - { - String escNameFilter = LuceneQueryParser.escape(nameFilter.replace('"', ' ')); - - query.append(" @cm\\:name:\"*" + escNameFilter + "*\"") - .append(" @cm\\:title:\"" + escNameFilter + "\"") - .append(" @cm\\:description:\"" + escNameFilter + "\""); - } - - if (sitePresetFilter != null && sitePresetFilter.length() > 0) - { - String escPresetFilter = LuceneQueryParser.escape(sitePresetFilter.replace('"', ' ')); - query.append(" @st\\:sitePreset:\"" + escPresetFilter + "\""); - } - - query.append(")"); - - ResultSet results = this.searchService.query( - siteRoot.getStoreRef(), - SearchService.LANGUAGE_LUCENE, - query.toString(), - null); - try - { - result = new ArrayList(results.length()); - for (NodeRef site : results.getNodeRefs()) - { - // Ignore any node type that is not a "site" - QName siteClassName = this.nodeService.getType(site); - if (this.dictionaryService.isSubClass(siteClassName, SiteModel.TYPE_SITE) == true) - { - result.add(createSiteInfo(site)); - // break on max size limit reached - if (result.size() == size) break; - } - } - } - finally - { - results.close(); - } - } - else - { - // Get ALL sites - this may be a very slow operation if there are many sites... - List assocs = this.nodeService.getChildAssocs( - siteRoot, ContentModel.ASSOC_CONTAINS, - RegexQNamePattern.MATCH_ALL); - result = new ArrayList(assocs.size()); - for (ChildAssociationRef assoc : assocs) - { - // Ignore any node type that is not a "site" - NodeRef site = assoc.getChildRef(); - QName siteClassName = this.nodeService.getType(site); - if (this.dictionaryService.isSubClass(siteClassName, SiteModel.TYPE_SITE) == true) - { - result.add(createSiteInfo(site)); - // break on max size limit reached - if (result.size() == size) break; - } - } - } - } - - return result; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String) - */ - public List listSites(final String userName) - { - // MT share - for activity service system callback - if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantUser(userName)) - { - final String tenantDomain = tenantService.getUserDomain(userName); - - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() - { - public List doWork() throws Exception - { - return listSitesImpl(userName); - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - } - else - { - return listSitesImpl(userName); - } - } - - private List listSitesImpl(String userName) - { - List result = null; - - // get the Groups this user is contained within (at any level) - Set groups = this.authorityService.getContainingAuthorities(null, userName, false); - Set siteNames = new HashSet(groups.size()); - // purge non Site related Groups and strip the group name down to the site "shortName" it relates too - for (String group : groups) - { - if (group.startsWith(GROUP_SITE_PREFIX)) - { - int roleIndex = group.lastIndexOf('_'); - String siteName; - if (roleIndex + 1 <= GROUP_SITE_PREFIX_LENGTH) - { - // There is no role associated - siteName = group.substring(GROUP_SITE_PREFIX_LENGTH); - } - else - { - siteName = group.substring(GROUP_SITE_PREFIX_LENGTH, roleIndex); - } - siteNames.add(siteName); - } - } - - // retrieve the site nodes based on the list from the containing site groups - NodeRef siteRoot = getSiteRoot(); - if (siteRoot == null) - { - result = new ArrayList(0); - } - else - { - List siteList = new ArrayList(siteNames); - // ensure we do not trip over the getChildrenByName() 1000 item API limit! - if (siteList.size() > 1000) - { - siteList = siteList.subList(0, 1000); - } - List assocs = this.nodeService.getChildrenByName( - siteRoot, - ContentModel.ASSOC_CONTAINS, - siteList); - result = new ArrayList(assocs.size()); - for (ChildAssociationRef assoc : assocs) - { - // Ignore any node that is not a "site" type - NodeRef site = assoc.getChildRef(); - QName siteClassName = this.nodeService.getType(site); - if (this.dictionaryService.isSubClass(siteClassName, SiteModel.TYPE_SITE)) - { - result.add(createSiteInfo(site)); - } - } - } - - return result; - } - - /** - * Creates a site information object given a site node reference - * - * @param siteNodeRef - * site node reference - * @return SiteInfo site information object - */ - private SiteInfo createSiteInfo(NodeRef siteNodeRef) - { - SiteInfo siteInfo = null; - - if (this.permissionService.hasPermission(siteNodeRef, PermissionService.READ_PROPERTIES).equals(AccessStatus.ALLOWED)) - { - // Get the properties - Map properties = this.nodeService.getProperties(siteNodeRef); - String shortName = (String) properties.get(ContentModel.PROP_NAME); - String sitePreset = (String) properties.get(PROP_SITE_PRESET); - String title = (String) properties.get(ContentModel.PROP_TITLE); - String description = (String) properties.get(ContentModel.PROP_DESCRIPTION); - - // Get the visibility of the site - SiteVisibility visibility = getSiteVisibility(siteNodeRef); - - // Create and return the site information - Map customProperties = getSiteCustomProperties(properties); - siteInfo = new SiteInfoImpl(sitePreset, shortName, title, description, visibility, customProperties, siteNodeRef); - } - - return siteInfo; - } - - /** - * Helper method to get the visibility of the site. If no value is present in the repository then it is calculated from the - * set permissions. This will maintain backwards compatibility with earlier versions of the service implementation. - * - * @param siteNodeRef site node reference - * @return SiteVisibility site visibility - */ - private SiteVisibility getSiteVisibility(NodeRef siteNodeRef) - { - SiteVisibility visibility = SiteVisibility.PRIVATE; - - // Get the visibility value stored in the repo - String visibilityValue = (String)this.nodeService.getProperty(siteNodeRef, SiteModel.PROP_SITE_VISIBILITY); - - // To maintain backwards compatibility calculate the visibility from the permissions - // if there is no value specified on the site node - if (visibilityValue == null) - { - // Examine each permission to see if this is a public site or not - Set permissions = this.permissionService.getAllSetPermissions(siteNodeRef); - for (AccessPermission permission : permissions) - { - if (permission.getAuthority().equals(PermissionService.ALL_AUTHORITIES) == true && - permission.getPermission().equals(SITE_CONSUMER) == true) - { - visibility = SiteVisibility.PUBLIC; - break; - } - } - } - else - { - // Create the enum value from the string - visibility = SiteVisibility.valueOf(visibilityValue); - } - - return visibility; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSite(java.lang.String) - */ - public SiteInfo getSite(final String shortName) - { - // MT share - for activity service system callback - if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantName(shortName)) - { - final String tenantDomain = tenantService.getDomain(shortName); - final String sName = tenantService.getBaseName(shortName, true); - - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public SiteInfo doWork() throws Exception - { - SiteInfo site = getSiteImpl(sName); - return new SiteInfoImpl(site.getSitePreset(), shortName, site.getTitle(), site.getDescription(), site.getVisibility(), site.getCustomProperties(), site.getNodeRef()); - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - } - else - { - return getSiteImpl(shortName); - } - } - - /** - * Get the site implementation given a short name - * - * @param shortName - * @return - */ - private SiteInfo getSiteImpl(String shortName) - { - SiteInfo result = null; - - // Get the site node - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef != null) - { - // Create the site info - result = createSiteInfo(siteNodeRef); - } - - // Return the site information - return result; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSite(org.alfresco.service.cmr.repository.NodeRef) - */ - public SiteInfo getSite(NodeRef nodeRef) - { - SiteInfo siteInfo = null; - NodeRef siteNodeRef = getSiteNodeRef(nodeRef); - if (siteNodeRef != null) - { - siteInfo = createSiteInfo(siteNodeRef); - } - return siteInfo; - } - - /** - * This method gets the st:site NodeRef for the Share Site which contains the given NodeRef. - * If the given NodeRef is not contained within a Share Site, then null is returned. - * - * @param nodeRef the node whose containing site is to be found. - * @return NodeRef site node reference or null if node is not in a site - */ - private NodeRef getSiteNodeRef(NodeRef nodeRef) - { - NodeRef siteNodeRef = null; - QName nodeRefType = nodeService.getType(nodeRef); - if (dictionaryService.isSubClass(nodeRefType, TYPE_SITE) == true) - { - siteNodeRef = nodeRef; - } - else - { - ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef); - if (primaryParent != null && primaryParent.getParentRef() != null) - { - siteNodeRef = getSiteNodeRef(primaryParent.getParentRef()); - } - } - return siteNodeRef; - } - - /** - * Gets the site's node reference based on its short name - * - * @param shortName short name - * - * @return NodeRef node reference - */ - private NodeRef getSiteNodeRef(final String shortName) - { - final String cacheKey = this.tenantAdminService.getCurrentUserDomain() + '_' + shortName; - NodeRef siteNodeRef = this.siteNodeRefs.get(cacheKey); - if (siteNodeRef != null) - { - // test for existance - and remove from cache if no longer exists - if (!this.nodeService.exists(siteNodeRef)) - { - this.siteNodeRefs.remove(cacheKey); - siteNodeRef = null; - } - } - else - { - // not in cache - find and store - final NodeRef siteRoot = getSiteParent(shortName); - - siteNodeRef = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public NodeRef doWork() throws Exception - { - // the site "short name" directly maps to the cm:name property - NodeRef siteNodeRef = nodeService.getChildByName(siteRoot, ContentModel.ASSOC_CONTAINS, shortName); - - // cache the result if found - null results will be required to ensure new sites are found later - if (siteNodeRef != null) - { - siteNodeRefs.put(cacheKey, siteNodeRef); - } - return siteNodeRef; - } - }, AuthenticationUtil.getSystemUserName()); - } - return siteNodeRef; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#updateSite(org.alfresco.service.cmr.site.SiteInfo) - */ - public void updateSite(SiteInfo siteInfo) - { - String shortName = siteInfo.getShortName(); - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_CAN_NOT_UPDATE, new Object[]{siteInfo.getShortName()}); - } - - // Get the sites properties - Map properties = this.nodeService.getProperties(siteNodeRef); - - // Update the properties of the site - // Note: the site preset and short name should never be updated! - properties.put(ContentModel.PROP_TITLE, siteInfo.getTitle()); - properties.put(ContentModel.PROP_DESCRIPTION, siteInfo.getDescription()); - - // Update the isPublic flag - SiteVisibility currentVisibility = getSiteVisibility(siteNodeRef); - SiteVisibility updatedVisibility = siteInfo.getVisibility(); - if (currentVisibility.equals(updatedVisibility) == false) - { - // visibility has changed - logger.debug("site:" + shortName + " visibility has changed from: " + currentVisibility + "to: " + updatedVisibility); - - // visibility has changed. - // Remove current visibility permissions - if (SiteVisibility.PUBLIC.equals(currentVisibility) == true) - { - this.permissionService.deletePermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER); - } - else if (SiteVisibility.MODERATED.equals(currentVisibility) == true) - { - this.permissionService.deletePermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER); - - /** - * update the containers - */ - List folders = fileFolderService.listFolders(siteNodeRef); - for(FileInfo folder : folders) - { - NodeRef containerNodeRef = folder.getNodeRef(); - this.permissionService.setInheritParentPermissions(containerNodeRef, true); - } - } - - // Add new visibility permissions - if (SiteVisibility.PUBLIC.equals(updatedVisibility) == true) - { - this.permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); - } - else if (SiteVisibility.MODERATED.equals(updatedVisibility) == true) - { - this.permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); - /** - * update the containers - */ - List folders = fileFolderService.listFolders(siteNodeRef); - for(FileInfo folder : folders) - { - NodeRef containerNodeRef = folder.getNodeRef(); - setModeratedPermissions(shortName, containerNodeRef); - } - } - - // Update the site node reference with the updated visibility value - properties.put(SiteModel.PROP_SITE_VISIBILITY, siteInfo.getVisibility().toString()); - } - - // Set the updated properties back onto the site node reference - this.nodeService.setProperties(siteNodeRef, properties); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#deleteSite(java.lang.String) - */ - public void deleteSite(final String shortName) - { - logger.debug("delete site :" + shortName); - final NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_CAN_NOT_DELETE, new Object[]{shortName}); - } - final QName siteType = nodeService.getType(siteNodeRef); - - // Delete the cached reference - String cacheKey = this.tenantAdminService.getCurrentUserDomain() + '_' + shortName; - this.siteNodeRefs.remove(cacheKey); - + public Comparator getRoleComparator() + { + return roleComparator; + } + + /** + * Checks that all necessary properties and services have been provided. + */ + public void init() + { + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "fileFolderService", fileFolderService); + PropertyCheck.mandatory(this, "searchService", searchService); + PropertyCheck.mandatory(this, "namespaceService", namespaceService); + PropertyCheck.mandatory(this, "permissionService", permissionService); + PropertyCheck.mandatory(this, "authenticationContext", authenticationContext); + PropertyCheck.mandatory(this, "personService", personService); + PropertyCheck.mandatory(this, "activityService", activityService); + PropertyCheck.mandatory(this, "taggingService", taggingService); + PropertyCheck.mandatory(this, "authorityService", authorityService); + PropertyCheck.mandatory(this, "sitesXPath", sitesXPath); + } + + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.site.SiteService#hasCreateSitePermissions() + */ + public boolean hasCreateSitePermissions() + { + final NodeRef siteRoot = getSiteRoot(); + if (siteRoot == null) + { + throw new SiteServiceException("No root sites folder exists"); + } + boolean result = permissionService.hasPermission(siteRoot, PermissionService.CONTRIBUTOR).equals(AccessStatus.ALLOWED); + return result; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#createSite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) + */ + public SiteInfo createSite(final String sitePreset, + String passedShortName, + final String title, + final String description, + final boolean isPublic) + { + // Determine the site visibility + SiteVisibility visibility = SiteVisibility.PRIVATE; + if (isPublic == true) + { + visibility = SiteVisibility.PUBLIC; + } + + // Create the site + return createSite(sitePreset, passedShortName, title, description, visibility); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#createSite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) + */ + public SiteInfo createSite(final String sitePreset, + String passedShortName, + final String title, + final String description, + final SiteVisibility visibility) + { + // Remove spaces from shortName + final String shortName = passedShortName.replaceAll(" ", ""); + + /** + * Check that the site does not already exist + */ + // Check to see if we already have a site of this name + NodeRef existingSite = getSiteNodeRef(shortName); + if (existingSite != null) + { + // Throw an exception since we have a duplicate site name + throw new SiteServiceException(MSG_UNABLE_TO_CREATE, new Object[]{shortName}); + } + + // Get the site parent node reference + NodeRef siteParent = getSiteParent(shortName); + if (siteParent == null) + { + throw new SiteServiceException("No root sites folder exists"); + } + + // Create the site node + PropertyMap properties = new PropertyMap(4); + properties.put(ContentModel.PROP_NAME, shortName); + properties.put(SiteModel.PROP_SITE_PRESET, sitePreset); + properties.put(SiteModel.PROP_SITE_VISIBILITY, visibility.toString()); + properties.put(ContentModel.PROP_TITLE, title); + properties.put(ContentModel.PROP_DESCRIPTION, description); + + final NodeRef siteNodeRef = this.nodeService.createNode( + siteParent, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + shortName), SiteModel.TYPE_SITE, properties) + .getChildRef(); + + // Make the new site a tag scope + this.taggingService.addTagScope(siteNodeRef); + + // Clear the sites inherited permissions + this.permissionService.setInheritParentPermissions(siteNodeRef, false); + + // Get the current user + final String currentUser = authenticationContext.getCurrentUserName(); + + // Create the relevant groups and assign permissions + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public String doWork() throws Exception + { + Set shareZones = new HashSet(2, 1.0f); + shareZones.add(AuthorityService.ZONE_APP_SHARE); + shareZones.add(AuthorityService.ZONE_AUTH_ALFRESCO); + + // Create the site's groups + String siteGroup = authorityService + .createAuthority(AuthorityType.GROUP, getSiteGroup(shortName, false), shortName, shareZones); + QName siteType = nodeService.getType(siteNodeRef); + Set permissions = permissionService.getSettablePermissions(siteType); + for (String permission : permissions) + { + // Create a group for the permission + String permissionGroup = authorityService.createAuthority(AuthorityType.GROUP, getSiteRoleGroup( + shortName, permission, false), shortName, shareZones); + authorityService.addAuthority(siteGroup, permissionGroup); + + // Assign the group the relevant permission on the site + permissionService.setPermission(siteNodeRef, permissionGroup, permission, true); + } + + // Set the memberships details + // - give all authorities site consumer if site is public + // - give all authorities read properties if site is moderated + // - give all authorities read permission on permissions so + // memberships can be calculated + // - add the current user to the site manager group + if (SiteVisibility.PUBLIC.equals(visibility) == true && + permissions.contains(SITE_CONSUMER)) + { + // From Alfresco 3.4 the 'site public' group is configurable. Out of the box it is + // GROUP_EVERYONE so unconfigured behaviour is unchanged. But from 3.4 admins + // can change the value of property site.public.group via JMX/properties files + // to be another group of their choosing. + // This then is the group that is given SiteConsumer access to newly created sites. + final String sitePublicGroup = sysAdminParams.getSitePublicGroup(); + + boolean groupExists = authorityService.authorityExists(sitePublicGroup); + if (!PermissionService.ALL_AUTHORITIES.equals(sitePublicGroup) && !groupExists) + { + // If the group does not exist, we cannot create the site. + throw new SiteServiceException(MSG_VISIBILITY_GROUP_MISSING, new Object[]{sitePublicGroup}); + } + + permissionService.setPermission(siteNodeRef, sitePublicGroup, SITE_CONSUMER, true); + } + else if (SiteVisibility.MODERATED.equals(visibility) == true && + permissions.contains(SITE_CONSUMER)) + { + // for moderated site EVERYONE has consumer access but site components do not. + permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); + } + permissionService.setPermission(siteNodeRef, + PermissionService.ALL_AUTHORITIES, + PermissionService.READ_PERMISSIONS, true); + authorityService.addAuthority(getSiteRoleGroup(shortName, + SiteModel.SITE_MANAGER, true), currentUser); + + // Return nothing + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + + // Return created site information + Map customProperties = getSiteCustomProperties(siteNodeRef); + SiteInfo siteInfo = new SiteInfoImpl(sitePreset, shortName, title, description, visibility, customProperties, siteNodeRef); + return siteInfo; + } + + /** + * Gets a map containing the site's custom properties + * + * @return Map map containing the custom properties of the site + */ + private Map getSiteCustomProperties(Map properties) + { + Map customProperties = new HashMap(4); + + for (Map.Entry entry : properties.entrySet()) + { + if (entry.getKey().getNamespaceURI().equals(SITE_CUSTOM_PROPERTY_URL) == true) + { + customProperties.put(entry.getKey(), entry.getValue()); + } + } + + return customProperties; + } + + /** + * Gets a map containing the site's custom properties + * + * @return Map map containing the custom properties of the site + */ + private Map getSiteCustomProperties(NodeRef siteNodeRef) + { + Map customProperties = new HashMap(4); + Map properties = nodeService.getProperties(siteNodeRef); + + for (Map.Entry entry : properties.entrySet()) + { + if (entry.getKey().getNamespaceURI().equals(SITE_CUSTOM_PROPERTY_URL) == true) + { + customProperties.put(entry.getKey(), entry.getValue()); + } + } + + return customProperties; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSiteGroup(java.lang.String) + */ + public String getSiteGroup(String shortName) + { + return getSiteGroup(shortName, true); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoleGroup(java.lang.String, + * java.lang.String) + */ + public String getSiteRoleGroup(String shortName, String role) + { + return getSiteRoleGroup(shortName, role, true); + } + + /** + * Helper method to get the name of the site group + * + * @param shortName site short name + * @return String site group name + */ + public String getSiteGroup(String shortName, boolean withGroupPrefix) + { + StringBuffer sb = new StringBuffer(64); + if (withGroupPrefix == true) + { + sb.append(PermissionService.GROUP_PREFIX); + } + sb.append(SITE_PREFIX); + sb.append(shortName); + return sb.toString(); + } + + /** + * Helper method to get the name of the site permission group + * + * @param shortName site short name + * @param permission permission name + * @param withGroupPrefix - should the name have the GROUP_ prefix? + * @return String site permission group name + */ + public String getSiteRoleGroup(String shortName, String permission, boolean withGroupPrefix) + { + return getSiteGroup(shortName, withGroupPrefix) + '_' + permission; + } + + /** + * Gets a sites parent folder based on it's short name + * + * @param shortName site short name + * @return NodeRef the site's parent + */ + private NodeRef getSiteParent(String shortName) + { + // TODO: For now just return the site root, later we may build folder + // structure based on the shortname to spread the sites about + return getSiteRoot(); + } + + /** + * {@inheritDoc} + */ + public NodeRef getSiteRoot() + { + String tenantDomain = tenantAdminService.getCurrentUserDomain(); + NodeRef siteHomeRef = siteHomeRefs.get(tenantDomain); + if (siteHomeRef == null) + { + siteHomeRef = AuthenticationUtil.runAs(new RunAsWork() + { + public NodeRef doWork() throws Exception + { + return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Exception + { + NodeRef result = null; + + // Get the root 'sites' folder + NodeRef rootNodeRef = nodeService.getRootNode(SITE_STORE); + List results = searchService.selectNodes( + rootNodeRef, + sitesXPath, + null, + namespaceService, + false, + SearchService.LANGUAGE_XPATH); + if (results.size() != 0) + { + result = results.get(0); + } + + return result; + } + }, true); + } + }, AuthenticationUtil.getSystemUserName()); + + // There may be domains with no sites (e.g. JSF-only clients). + if (siteHomeRef != null) + { + siteHomeRefs.put(tenantDomain, siteHomeRef); + } + } + return siteHomeRef; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String, java.lang.String) + */ + public List listSites(String nameFilter, String sitePresetFilter) + { + return listSites(nameFilter, sitePresetFilter, 0); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String, java.lang.String, int) + */ + public List listSites(String nameFilter, String sitePresetFilter, int size) + { + List result; + + NodeRef siteRoot = getSiteRoot(); + if (siteRoot == null) + { + result = Collections.emptyList(); + } + else + { + if (nameFilter != null && nameFilter.length() != 0 || sitePresetFilter != null && sitePresetFilter.length() > 0) + { + // get the sites that match the specified names + StringBuilder query = new StringBuilder(128); + query.append("+PARENT:\"").append(siteRoot.toString()) + .append("\" +("); + + if (nameFilter != null && nameFilter.length() > 0) + { + String escNameFilter = LuceneQueryParser.escape(nameFilter.replace('"', ' ')); + + query.append(" @cm\\:name:\"*" + escNameFilter + "*\"") + .append(" @cm\\:title:\"" + escNameFilter + "\"") + .append(" @cm\\:description:\"" + escNameFilter + "\""); + } + + if (sitePresetFilter != null && sitePresetFilter.length() > 0) + { + String escPresetFilter = LuceneQueryParser.escape(sitePresetFilter.replace('"', ' ')); + query.append(" @st\\:sitePreset:\"" + escPresetFilter + "\""); + } + + query.append(")"); + + ResultSet results = this.searchService.query( + siteRoot.getStoreRef(), + SearchService.LANGUAGE_LUCENE, + query.toString(), + null); + try + { + result = new ArrayList(results.length()); + for (NodeRef site : results.getNodeRefs()) + { + // Ignore any node type that is not a "site" + QName siteClassName = this.nodeService.getType(site); + if (this.dictionaryService.isSubClass(siteClassName, SiteModel.TYPE_SITE) == true) + { + result.add(createSiteInfo(site)); + // break on max size limit reached + if (result.size() == size) break; + } + } + } + finally + { + results.close(); + } + } + else + { + // Get ALL sites - this may be a very slow operation if there are many sites... + List assocs = this.nodeService.getChildAssocs( + siteRoot, ContentModel.ASSOC_CONTAINS, + RegexQNamePattern.MATCH_ALL); + result = new ArrayList(assocs.size()); + for (ChildAssociationRef assoc : assocs) + { + // Ignore any node type that is not a "site" + NodeRef site = assoc.getChildRef(); + QName siteClassName = this.nodeService.getType(site); + if (this.dictionaryService.isSubClass(siteClassName, SiteModel.TYPE_SITE) == true) + { + result.add(createSiteInfo(site)); + // break on max size limit reached + if (result.size() == size) break; + } + } + } + } + + return result; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String) + */ + public List listSites(final String userName) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantUser(userName)) + { + final String tenantDomain = tenantService.getUserDomain(userName); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public List doWork() throws Exception + { + return listSitesImpl(userName); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + else + { + return listSitesImpl(userName); + } + } + + private List listSitesImpl(String userName) + { + List result = null; + + // get the Groups this user is contained within (at any level) + Set groups = this.authorityService.getContainingAuthorities(null, userName, false); + Set siteNames = new HashSet(groups.size()); + // purge non Site related Groups and strip the group name down to the site "shortName" it relates too + for (String group : groups) + { + if (group.startsWith(GROUP_SITE_PREFIX)) + { + int roleIndex = group.lastIndexOf('_'); + String siteName; + if (roleIndex + 1 <= GROUP_SITE_PREFIX_LENGTH) + { + // There is no role associated + siteName = group.substring(GROUP_SITE_PREFIX_LENGTH); + } + else + { + siteName = group.substring(GROUP_SITE_PREFIX_LENGTH, roleIndex); + } + siteNames.add(siteName); + } + } + + // retrieve the site nodes based on the list from the containing site groups + NodeRef siteRoot = getSiteRoot(); + if (siteRoot == null) + { + result = new ArrayList(0); + } + else + { + List siteList = new ArrayList(siteNames); + // ensure we do not trip over the getChildrenByName() 1000 item API limit! + if (siteList.size() > 1000) + { + siteList = siteList.subList(0, 1000); + } + List assocs = this.nodeService.getChildrenByName( + siteRoot, + ContentModel.ASSOC_CONTAINS, + siteList); + result = new ArrayList(assocs.size()); + for (ChildAssociationRef assoc : assocs) + { + // Ignore any node that is not a "site" type + NodeRef site = assoc.getChildRef(); + QName siteClassName = this.nodeService.getType(site); + if (this.dictionaryService.isSubClass(siteClassName, SiteModel.TYPE_SITE)) + { + result.add(createSiteInfo(site)); + } + } + } + + return result; + } + + /** + * Creates a site information object given a site node reference + * + * @param siteNodeRef + * site node reference + * @return SiteInfo site information object + */ + private SiteInfo createSiteInfo(NodeRef siteNodeRef) + { + SiteInfo siteInfo = null; + + if (this.permissionService.hasPermission(siteNodeRef, PermissionService.READ_PROPERTIES).equals(AccessStatus.ALLOWED)) + { + // Get the properties + Map properties = this.nodeService.getProperties(siteNodeRef); + String shortName = (String) properties.get(ContentModel.PROP_NAME); + String sitePreset = (String) properties.get(PROP_SITE_PRESET); + String title = (String) properties.get(ContentModel.PROP_TITLE); + String description = (String) properties.get(ContentModel.PROP_DESCRIPTION); + + // Get the visibility of the site + SiteVisibility visibility = getSiteVisibility(siteNodeRef); + + // Create and return the site information + Map customProperties = getSiteCustomProperties(properties); + siteInfo = new SiteInfoImpl(sitePreset, shortName, title, description, visibility, customProperties, siteNodeRef); + } + + return siteInfo; + } + + /** + * Helper method to get the visibility of the site. If no value is present in the repository then it is calculated from the + * set permissions. This will maintain backwards compatibility with earlier versions of the service implementation. + * + * @param siteNodeRef site node reference + * @return SiteVisibility site visibility + */ + private SiteVisibility getSiteVisibility(NodeRef siteNodeRef) + { + SiteVisibility visibility = SiteVisibility.PRIVATE; + + // Get the visibility value stored in the repo + String visibilityValue = (String)this.nodeService.getProperty(siteNodeRef, SiteModel.PROP_SITE_VISIBILITY); + + // To maintain backwards compatibility calculate the visibility from the permissions + // if there is no value specified on the site node + if (visibilityValue == null) + { + // Examine each permission to see if this is a public site or not + Set permissions = this.permissionService.getAllSetPermissions(siteNodeRef); + for (AccessPermission permission : permissions) + { + if (permission.getAuthority().equals(PermissionService.ALL_AUTHORITIES) == true && + permission.getPermission().equals(SITE_CONSUMER) == true) + { + visibility = SiteVisibility.PUBLIC; + break; + } + } + } + else + { + // Create the enum value from the string + visibility = SiteVisibility.valueOf(visibilityValue); + } + + return visibility; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSite(java.lang.String) + */ + public SiteInfo getSite(final String shortName) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantName(shortName)) + { + final String tenantDomain = tenantService.getDomain(shortName); + final String sName = tenantService.getBaseName(shortName, true); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public SiteInfo doWork() throws Exception + { + SiteInfo site = getSiteImpl(sName); + return new SiteInfoImpl(site.getSitePreset(), shortName, site.getTitle(), site.getDescription(), site.getVisibility(), site.getCustomProperties(), site.getNodeRef()); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + else + { + return getSiteImpl(shortName); + } + } + + /** + * Get the site implementation given a short name + * + * @param shortName + * @return + */ + private SiteInfo getSiteImpl(String shortName) + { + SiteInfo result = null; + + // Get the site node + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef != null) + { + // Create the site info + result = createSiteInfo(siteNodeRef); + } + + // Return the site information + return result; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSite(org.alfresco.service.cmr.repository.NodeRef) + */ + public SiteInfo getSite(NodeRef nodeRef) + { + SiteInfo siteInfo = null; + NodeRef siteNodeRef = getSiteNodeRef(nodeRef); + if (siteNodeRef != null) + { + siteInfo = createSiteInfo(siteNodeRef); + } + return siteInfo; + } + + /** + * This method gets the st:site NodeRef for the Share Site which contains the given NodeRef. + * If the given NodeRef is not contained within a Share Site, then null is returned. + * + * @param nodeRef the node whose containing site is to be found. + * @return NodeRef site node reference or null if node is not in a site + */ + private NodeRef getSiteNodeRef(NodeRef nodeRef) + { + NodeRef siteNodeRef = null; + QName nodeRefType = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(nodeRefType, TYPE_SITE) == true) + { + siteNodeRef = nodeRef; + } + else + { + ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef); + if (primaryParent != null && primaryParent.getParentRef() != null) + { + siteNodeRef = getSiteNodeRef(primaryParent.getParentRef()); + } + } + return siteNodeRef; + } + + /** + * Gets the site's node reference based on its short name + * + * @param shortName short name + * + * @return NodeRef node reference + */ + private NodeRef getSiteNodeRef(final String shortName) + { + final String cacheKey = this.tenantAdminService.getCurrentUserDomain() + '_' + shortName; + NodeRef siteNodeRef = this.siteNodeRefs.get(cacheKey); + if (siteNodeRef != null) + { + // test for existance - and remove from cache if no longer exists + if (!this.nodeService.exists(siteNodeRef)) + { + this.siteNodeRefs.remove(cacheKey); + siteNodeRef = null; + } + } + else + { + // not in cache - find and store + final NodeRef siteRoot = getSiteParent(shortName); + + siteNodeRef = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public NodeRef doWork() throws Exception + { + // the site "short name" directly maps to the cm:name property + NodeRef siteNode = nodeService.getChildByName(siteRoot, ContentModel.ASSOC_CONTAINS, shortName); + + // cache the result if found - null results will be required to ensure new sites are found later + if (siteNode != null) + { + siteNodeRefs.put(cacheKey, siteNode); + } + return siteNode; + } + }, AuthenticationUtil.getSystemUserName()); + } + return siteNodeRef; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#updateSite(org.alfresco.service.cmr.site.SiteInfo) + */ + public void updateSite(SiteInfo siteInfo) + { + String shortName = siteInfo.getShortName(); + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_CAN_NOT_UPDATE, new Object[]{siteInfo.getShortName()}); + } + + // Get the sites properties + Map properties = this.nodeService.getProperties(siteNodeRef); + + // Update the properties of the site + // Note: the site preset and short name should never be updated! + properties.put(ContentModel.PROP_TITLE, siteInfo.getTitle()); + properties.put(ContentModel.PROP_DESCRIPTION, siteInfo.getDescription()); + + // Update the isPublic flag + SiteVisibility currentVisibility = getSiteVisibility(siteNodeRef); + SiteVisibility updatedVisibility = siteInfo.getVisibility(); + if (currentVisibility.equals(updatedVisibility) == false) + { + // visibility has changed + logger.debug("site:" + shortName + " visibility has changed from: " + currentVisibility + "to: " + updatedVisibility); + + // visibility has changed. + // Remove current visibility permissions + if (SiteVisibility.PUBLIC.equals(currentVisibility) == true) + { + this.permissionService.deletePermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER); + } + else if (SiteVisibility.MODERATED.equals(currentVisibility) == true) + { + this.permissionService.deletePermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER); + + /** + * update the containers + */ + List folders = fileFolderService.listFolders(siteNodeRef); + for(FileInfo folder : folders) + { + NodeRef containerNodeRef = folder.getNodeRef(); + this.permissionService.setInheritParentPermissions(containerNodeRef, true); + } + } + + // Add new visibility permissions + if (SiteVisibility.PUBLIC.equals(updatedVisibility) == true) + { + this.permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); + } + else if (SiteVisibility.MODERATED.equals(updatedVisibility) == true) + { + this.permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); + /** + * update the containers + */ + List folders = fileFolderService.listFolders(siteNodeRef); + for(FileInfo folder : folders) + { + NodeRef containerNodeRef = folder.getNodeRef(); + setModeratedPermissions(shortName, containerNodeRef); + } + } + + // Update the site node reference with the updated visibility value + properties.put(SiteModel.PROP_SITE_VISIBILITY, siteInfo.getVisibility().toString()); + } + + // Set the updated properties back onto the site node reference + this.nodeService.setProperties(siteNodeRef, properties); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#deleteSite(java.lang.String) + */ + public void deleteSite(final String shortName) + { + logger.debug("delete site :" + shortName); + final NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_CAN_NOT_DELETE, new Object[]{shortName}); + } + final QName siteType = nodeService.getType(siteNodeRef); + + // Delete the cached reference + String cacheKey = this.tenantAdminService.getCurrentUserDomain() + '_' + shortName; + this.siteNodeRefs.remove(cacheKey); + // The default behaviour is that sites cannot be deleted. But we disable that behaviour here // in order to allow site deletion only via this service. Share calls this service for deletion. // @@ -1120,611 +1117,612 @@ public class SiteServiceImpl implements SiteService, SiteModel { behaviourFilter.enableBehaviour(siteNodeRef, ContentModel.ASPECT_UNDELETABLE); } - - // Delete the associated groups - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public Object doWork() throws Exception - { - // Delete the master site group - authorityService.deleteAuthority(getSiteGroup(shortName, true), false); - - // Iterate over the role related groups and delete then - Set permissions = permissionService.getSettablePermissions(siteType); - for (String permission : permissions) - { - String siteRoleGroup = getSiteRoleGroup(shortName, permission, true); - authorityService.deleteAuthority(siteRoleGroup); - } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - - logger.debug("site deleted :" + shortName); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#listMembers(java.lang.String, java.lang.String, java.lang.String, int) - */ - public Map listMembers(String shortName, String nameFilter, String roleFilter, int size) - { - return listMembers(shortName, nameFilter, roleFilter, size, false); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#listMembers(String, String, String, int, boolean) - */ - public Map listMembers(String shortName, final String nameFilter, final String roleFilter, final int size, final boolean collapseGroups) - { - // MT share - for activity service system callback - if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantName(shortName)) - { - final String tenantDomain = tenantService.getDomain(shortName); - final String sName = tenantService.getBaseName(shortName, true); - - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() - { - public Map doWork() throws Exception - { - return listMembersImpl(sName, nameFilter, roleFilter, size, collapseGroups); - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - } - else - { - return listMembersImpl(shortName, nameFilter, roleFilter, size, collapseGroups); - } - } - - private Map listMembersImpl(String shortName, String nameFilter, String roleFilter, int size, boolean collapseGroups) - { - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); - } - - // build an array of name filter tokens pre lowercased to test against person properties - String[] nameFilters = new String[0]; - if (nameFilter != null && nameFilter.length() != 0) - { - StringTokenizer t = new StringTokenizer(nameFilter, " "); - nameFilters = new String[t.countTokens()]; - for (int i=0; t.hasMoreTokens(); i++) - { - nameFilters[i] = t.nextToken().toLowerCase(); - } - } - - Map members = new HashMap(32); - - QName siteType = nodeService.getType(siteNodeRef); - Set permissions = this.permissionService.getSettablePermissions(siteType); - for (String permission : permissions) - { - if (roleFilter == null || roleFilter.length() == 0 || roleFilter.equals(permission)) - { - String groupName = getSiteRoleGroup(shortName, permission, true); - Set users = this.authorityService.getContainedAuthorities(AuthorityType.USER, groupName, true); - for (String user : users) - { - boolean addUser = true; - if (nameFilter != null && nameFilter.length() != 0 && !nameFilter.equals(user)) - { - // found a filter - does it match person first/last name? - addUser = matchPerson(nameFilters, user); - } - if (addUser) - { - // Add the user and their permission to the returned map - members.put(user, permission); - - // break on max size limit reached - if (members.size() == size) break; - } - } - - Set groups = this.authorityService.getContainedAuthorities(AuthorityType.GROUP, groupName, true); - for (String group : groups) - { - if (collapseGroups == false) - { - if (nameFilter != null && nameFilter.length() != 0) - { - // found a filter - does it match Group name part? - if (group.substring(GROUP_PREFIX_LENGTH).toLowerCase().contains(nameFilter.toLowerCase())) - { - members.put(group, permission); - } - } - else - { - // No name filter add this group - members.put(group, permission); - } - } - else - { - Set subUsers = this.authorityService.getContainedAuthorities(AuthorityType.USER, group, false); - for (String subUser : subUsers) - { - boolean addUser = true; - if (nameFilter != null && nameFilter.length() != 0 && !nameFilter.equals(subUser)) - { - // found a filter - does it match person first/last name? - addUser = matchPerson(nameFilters, subUser); - } - if (addUser) - { - // Add the collapsed user into the members list if they do not already appear in the list - if (members.containsKey(subUser) == false) - { - members.put(subUser, permission); - } - - // break on max size limit reached - if (members.size() == size) break; - } - } - } - } - } - } - - return members; - } - - /** - * Helper to match name filters to Person properties - * - * @param filter - * @param username - * @return - */ - private boolean matchPerson(final String[] nameFilters, final String username) - { - boolean addUser = false; - - String query = "+TYPE:\"cm:person\" +@cm\\:userName:\"" + username + "\""; - SearchParameters searchParameters = new SearchParameters(); - searchParameters.setLanguage(SearchService.LANGUAGE_LUCENE); - searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); - searchParameters.setQuery(query); - ResultSet resultSet = this.searchService.query(searchParameters); - try - { - if (resultSet.length() != 0) - { - ResultSetRow row = resultSet.getRow(0); - Map values = row.getValues(); - String firstName = (String)values.get(ContentModel.PROP_FIRSTNAME.toString()); - String lastName = (String)values.get(ContentModel.PROP_LASTNAME.toString()); - - final String lowFirstName = (firstName != null ? firstName.toLowerCase() : ""); - final String lowLastName = (lastName != null ? lastName.toLowerCase() : ""); - for (int i=0; i roles = getMembersRoles(shortName, authorityName); - if (roles.size() != 0) - { - if (roles.size() > 1 && roleComparator != null) - { - // Need to sort the roles into the most important first. - SortedSet sortedRoles = new TreeSet(roleComparator); - for (String role : roles) - { - sortedRoles.add(role); - } - result = sortedRoles.first(); - } - else - { - // don't search on precedence or only one result - result = roles.get(0); - } - } - return result; - } - - public List getMembersRoles(String shortName, String authorityName) - { - List result = new ArrayList(5); - List groups = getPermissionGroups(shortName, authorityName); - for (String group : groups) - { - int index = group.lastIndexOf('_'); - if (index != -1) - { - result.add(group.substring(index + 1)); - } - } - return result; - } - - /** - * Helper method to get the permission groups for a given authority on a site. - * Returns empty List if the user does not have a explicit membership to the site. - * - * A user permission will take precedence over a permission obtained via a group. - * - * @param siteShortName site short name - * @param authorityName authority name - * @return List Permission groups, empty list if no explicit membership set - */ - private List getPermissionGroups(String siteShortName, String authorityName) - { - NodeRef siteNodeRef = getSiteNodeRef(siteShortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[] { siteShortName }); - } - + + // Delete the associated groups + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Delete the master site group + authorityService.deleteAuthority(getSiteGroup(shortName, true), false); + + // Iterate over the role related groups and delete then + Set permissions = permissionService.getSettablePermissions(siteType); + for (String permission : permissions) + { + String siteRoleGroup = getSiteRoleGroup(shortName, permission, true); + authorityService.deleteAuthority(siteRoleGroup); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + logger.debug("site deleted :" + shortName); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#listMembers(java.lang.String, java.lang.String, java.lang.String, int) + */ + public Map listMembers(String shortName, String nameFilter, String roleFilter, int size) + { + return listMembers(shortName, nameFilter, roleFilter, size, false); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#listMembers(String, String, String, int, boolean) + */ + public Map listMembers(String shortName, final String nameFilter, final String roleFilter, final int size, final boolean collapseGroups) + { + // MT share - for activity service system callback + if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantName(shortName)) + { + final String tenantDomain = tenantService.getDomain(shortName); + final String sName = tenantService.getBaseName(shortName, true); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + { + public Map doWork() throws Exception + { + return listMembersImpl(sName, nameFilter, roleFilter, size, collapseGroups); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + else + { + return listMembersImpl(shortName, nameFilter, roleFilter, size, collapseGroups); + } + } + + private Map listMembersImpl(String shortName, String nameFilter, String roleFilter, int size, boolean collapseGroups) + { + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); + } + + // build an array of name filter tokens pre lowercased to test against person properties + String[] nameFilters = new String[0]; + if (nameFilter != null && nameFilter.length() != 0) + { + StringTokenizer t = new StringTokenizer(nameFilter, " "); + nameFilters = new String[t.countTokens()]; + for (int i=0; t.hasMoreTokens(); i++) + { + nameFilters[i] = t.nextToken().toLowerCase(); + } + } + + Map members = new HashMap(32); + + QName siteType = nodeService.getType(siteNodeRef); + Set permissions = this.permissionService.getSettablePermissions(siteType); + for (String permission : permissions) + { + if (roleFilter == null || roleFilter.length() == 0 || roleFilter.equals(permission)) + { + String groupName = getSiteRoleGroup(shortName, permission, true); + Set users = this.authorityService.getContainedAuthorities(AuthorityType.USER, groupName, true); + for (String user : users) + { + boolean addUser = true; + if (nameFilter != null && nameFilter.length() != 0 && !nameFilter.equals(user)) + { + // found a filter - does it match person first/last name? + addUser = matchPerson(nameFilters, user); + } + if (addUser) + { + // Add the user and their permission to the returned map + members.put(user, permission); + + // break on max size limit reached + if (members.size() == size) break; + } + } + + Set groups = this.authorityService.getContainedAuthorities(AuthorityType.GROUP, groupName, true); + for (String group : groups) + { + if (collapseGroups == false) + { + if (nameFilter != null && nameFilter.length() != 0) + { + // found a filter - does it match Group name part? + if (group.substring(GROUP_PREFIX_LENGTH).toLowerCase().contains(nameFilter.toLowerCase())) + { + members.put(group, permission); + } + } + else + { + // No name filter add this group + members.put(group, permission); + } + } + else + { + Set subUsers = this.authorityService.getContainedAuthorities(AuthorityType.USER, group, false); + for (String subUser : subUsers) + { + boolean addUser = true; + if (nameFilter != null && nameFilter.length() != 0 && !nameFilter.equals(subUser)) + { + // found a filter - does it match person first/last name? + addUser = matchPerson(nameFilters, subUser); + } + if (addUser) + { + // Add the collapsed user into the members list if they do not already appear in the list + if (members.containsKey(subUser) == false) + { + members.put(subUser, permission); + } + + // break on max size limit reached + if (members.size() == size) break; + } + } + } + } + } + } + + return members; + } + + /** + * Helper to match name filters to Person properties + * + * @param filter + * @param username + * @return + */ + private boolean matchPerson(final String[] nameFilters, final String username) + { + boolean addUser = false; + + String query = "+TYPE:\"cm:person\" +@cm\\:userName:\"" + username + "\""; + SearchParameters searchParameters = new SearchParameters(); + searchParameters.setLanguage(SearchService.LANGUAGE_LUCENE); + searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + searchParameters.setQuery(query); + ResultSet resultSet = this.searchService.query(searchParameters); + try + { + if (resultSet.length() != 0) + { + ResultSetRow row = resultSet.getRow(0); + Map values = row.getValues(); + String firstName = (String)values.get(ContentModel.PROP_FIRSTNAME.toString()); + String lastName = (String)values.get(ContentModel.PROP_LASTNAME.toString()); + + final String lowFirstName = (firstName != null ? firstName.toLowerCase() : ""); + final String lowLastName = (lastName != null ? lastName.toLowerCase() : ""); + for (int i=0; i roles = getMembersRoles(shortName, authorityName); + if (roles.size() != 0) + { + if (roles.size() > 1 && roleComparator != null) + { + // Need to sort the roles into the most important first. + SortedSet sortedRoles = new TreeSet(roleComparator); + for (String role : roles) + { + sortedRoles.add(role); + } + result = sortedRoles.first(); + } + else + { + // don't search on precedence or only one result + result = roles.get(0); + } + } + return result; + } + + public List getMembersRoles(String shortName, String authorityName) + { + List result = new ArrayList(5); + List groups = getPermissionGroups(shortName, authorityName); + for (String group : groups) + { + int index = group.lastIndexOf('_'); + if (index != -1) + { + result.add(group.substring(index + 1)); + } + } + return result; + } + + /** + * Helper method to get the permission groups for a given authority on a site. + * Returns empty List if the user does not have a explicit membership to the site. + * + * A user permission will take precedence over a permission obtained via a group. + * + * @param siteShortName site short name + * @param authorityName authority name + * @return List Permission groups, empty list if no explicit membership set + */ + private List getPermissionGroups(String siteShortName, String authorityName) + { + NodeRef siteNodeRef = getSiteNodeRef(siteShortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[] { siteShortName }); + } + List fullResult = new ArrayList(5); - QName siteType = nodeService.getType(siteNodeRef); + QName siteType = nodeService.getType(siteNodeRef); Set roles = this.permissionService.getSettablePermissions(siteType); // First use the authority's cached recursive group memberships to answer the question quickly Set authorityGroups = this.authorityService.getContainingAuthorities(AuthorityType.GROUP, authorityName, false); - for (String role : roles) - { - String roleGroup = getSiteRoleGroup(siteShortName, role, true); + for (String role : roles) + { + String roleGroup = getSiteRoleGroup(siteShortName, role, true); if (authorityGroups.contains(roleGroup)) - { + { fullResult.add(roleGroup); - } - } - + } + } + // Unfortunately, due to direct membership taking precendence, we can't answer the question quickly if more than one role has been inherited if (fullResult.size() <= 1) - { + { return fullResult; - } - + } + // Check direct group memberships List result = new ArrayList(5); authorityGroups = this.authorityService.getContainingAuthorities(AuthorityType.GROUP, authorityName, true); - for (String role : roles) - { - String roleGroup = getSiteRoleGroup(siteShortName, role, true); + for (String role : roles) + { + String roleGroup = getSiteRoleGroup(siteShortName, role, true); if (authorityGroups.contains(roleGroup)) - { - result.add(roleGroup); - } - } + { + result.add(roleGroup); + } + } // If there are user permissions then they take priority return result.size() > 0 ? result : fullResult; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles() - */ - public List getSiteRoles() - { - return getSiteRoles(SiteModel.TYPE_SITE); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles(String) - */ - public List getSiteRoles(String shortName) - { - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[] { shortName }); - } - QName siteType = nodeService.getType(siteNodeRef); - return getSiteRoles(siteType); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles() - * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles(String) - */ - public List getSiteRoles(QName type) - { - Set permissions = permissionService.getSettablePermissions(type); - return new ArrayList(permissions); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#isMember(java.lang.String, java.lang.String) - */ - public boolean isMember(String shortName, String authorityName) - { - return (!getPermissionGroups(shortName, authorityName).isEmpty()); - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#removeMembership(java.lang.String, java.lang.String) - */ - public void removeMembership(final String shortName, final String authorityName) - { - final NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); - } - - // TODO what do we do about the user if they are in a group that has - // rights to the site? - - // Get the current user - String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); - - // Get the user current role - final String role = getMembersRole(shortName, authorityName); - if (role != null) - { - // Check that we are not about to remove the last site manager - checkLastManagerRemoval(shortName, authorityName, role); - - // If ... - // -- the current user has change permissions rights on the site - // or - // -- the user is ourselves - if ((currentUserName.equals(authorityName) == true) || - (permissionService.hasPermission(siteNodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)) - { - // Run as system user - AuthenticationUtil.runAs( - new AuthenticationUtil.RunAsWork() - { - public Object doWork() throws Exception - { - // Remove the user from the current permission - // group - String currentGroup = getSiteRoleGroup(shortName, role, true); - authorityService.removeAuthority(currentGroup, authorityName); - - return null; - } - }, AuthenticationUtil.SYSTEM_USER_NAME); - - // Raise events - AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName); - if (authorityType == AuthorityType.USER) - { - activityService.postActivity( - ActivityType.SITE_USER_REMOVED, shortName, - ACTIVITY_TOOL, getActivityUserData(authorityName, "")); - } - else if (authorityType == AuthorityType.GROUP) - { - activityService.postActivity( - ActivityType.SITE_GROUP_REMOVED, shortName, - ACTIVITY_TOOL, getActivityGroupData(authorityName, "")); - } - } - else - { - // Throw an exception - throw new SiteServiceException(MSG_CAN_NOT_REMOVE_MSHIP, new Object[]{shortName}); - } - } - else - { - // Throw an exception - throw new SiteServiceException(MSG_CAN_NOT_REMOVE_MSHIP, new Object[]{shortName}); - } - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#setMembership(java.lang.String, - * java.lang.String, java.lang.String) - */ - public void setMembership(final String shortName, - final String authorityName, - final String role) - { - final NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); - } - - // Get the user's current role - final String currentRole = getMembersRole(shortName, authorityName); - - // Do nothing if the role of the user is not being changed - if (currentRole == null || role.equals(currentRole) == false) - { - // TODO if this is the only site manager do not down grade their - // permissions - - // Get the visibility of the site - SiteVisibility visibility = getSiteVisibility(siteNodeRef); - - // If we are ... - // -- the current user has change permissions rights on the site - // or we are ... - // -- referring to a public site and - // -- the role being set is consumer and - // -- the user being added is ourselves and - // -- the member does not already have permissions - // ... then we can set the permissions as system user - final String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); - if ((permissionService.hasPermission(siteNodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) || - (SiteVisibility.PUBLIC.equals(visibility) == true && - role.equals(SiteModel.SITE_CONSUMER) == true && - authorityName.equals(currentUserName) == true && - currentRole == null)) - { - // Check that we are not about to remove the last site manager - checkLastManagerRemoval(shortName, authorityName, currentRole); - - // Run as system user - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public Object doWork() throws Exception - { - if (currentRole != null) - { - // Remove the user from the current - // permission group - String currentGroup = getSiteRoleGroup(shortName, currentRole, true); - authorityService.removeAuthority(currentGroup, authorityName); - } - - // Add the user to the new permission group - String newGroup = getSiteRoleGroup(shortName, role, true); - authorityService.addAuthority(newGroup, authorityName); - - return null; - } - - }, AuthenticationUtil.SYSTEM_USER_NAME); - - if (currentRole == null) - { - AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName); - if (authorityType == AuthorityType.USER) - { - activityService.postActivity( - ActivityType.SITE_USER_JOINED, shortName, - ACTIVITY_TOOL, getActivityUserData(authorityName, role)); - } - else if (authorityType == AuthorityType.GROUP) - { - activityService.postActivity( - ActivityType.SITE_GROUP_ADDED, shortName, - ACTIVITY_TOOL, getActivityGroupData(authorityName, role)); - } - } - else - { - AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName); - if (authorityType == AuthorityType.USER) - { - activityService.postActivity( - ActivityType.SITE_USER_ROLE_UPDATE, shortName, - ACTIVITY_TOOL, getActivityUserData(authorityName, role)); - } - else if (authorityType == AuthorityType.GROUP) - { - activityService.postActivity( - ActivityType.SITE_GROUP_ROLE_UPDATE, shortName, - ACTIVITY_TOOL, getActivityGroupData(authorityName, role)); - } - } - } - else - { - // Raise a permission exception - throw new SiteServiceException(MSG_CAN_NOT_CHANGE_MSHIP, new Object[]{shortName}); - } - } - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#createContainer(java.lang.String, - * java.lang.String, org.alfresco.service.namespace.QName, - * java.util.Map) - */ - public NodeRef createContainer(String shortName, - String componentId, - QName containerType, - Map containerProperties) - { - // Check for the component id - ParameterCheck.mandatoryString("componentId", componentId); - - // retrieve site - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); - } - - // Update the isPublic flag - SiteVisibility siteVisibility = getSiteVisibility(siteNodeRef); - - // retrieve component folder within site - NodeRef containerNodeRef = null; - try - { - containerNodeRef = findContainer(siteNodeRef, componentId); - } - catch (FileNotFoundException e) - { - } - - // create the container node reference - if (containerNodeRef == null) - { - if (containerType == null) - { - containerType = ContentModel.TYPE_FOLDER; - } - - // create component folder - FileInfo fileInfo = fileFolderService.create(siteNodeRef, - componentId, containerType); - - // Get the created container - containerNodeRef = fileInfo.getNodeRef(); - - // Set the properties if they have been provided - if (containerProperties != null) - { - Map props = this.nodeService - .getProperties(containerNodeRef); - props.putAll(containerProperties); - this.nodeService.setProperties(containerNodeRef, props); - } - - // Add the container aspect - Map aspectProps = new HashMap(1, 1.0f); - aspectProps.put(SiteModel.PROP_COMPONENT_ID, componentId); - this.nodeService.addAspect(containerNodeRef, ASPECT_SITE_CONTAINER, - aspectProps); - - // Set permissions on the container - if(SiteVisibility.MODERATED.equals(siteVisibility)) - { - setModeratedPermissions(shortName, containerNodeRef); - } - - // Make the container a tag scope - this.taggingService.addTagScope(containerNodeRef); - } - - return containerNodeRef; - } - - /** + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles() + */ + public List getSiteRoles() + { + return getSiteRoles(SiteModel.TYPE_SITE); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles(String) + */ + public List getSiteRoles(String shortName) + { + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[] { shortName }); + } + QName siteType = nodeService.getType(siteNodeRef); + return getSiteRoles(siteType); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles() + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles(String) + */ + public List getSiteRoles(QName type) + { + Set permissions = permissionService.getSettablePermissions(type); + return new ArrayList(permissions); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#isMember(java.lang.String, java.lang.String) + */ + public boolean isMember(String shortName, String authorityName) + { + return (!getPermissionGroups(shortName, authorityName).isEmpty()); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#removeMembership(java.lang.String, java.lang.String) + */ + public void removeMembership(final String shortName, final String authorityName) + { + final NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); + } + + // TODO what do we do about the user if they are in a group that has + // rights to the site? + + // Get the current user + String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); + + // Get the user current role + final String role = getMembersRole(shortName, authorityName); + if (role != null) + { + // Check that we are not about to remove the last site manager + checkLastManagerRemoval(shortName, authorityName, role); + + // If ... + // -- the current user has change permissions rights on the site + // or + // -- the user is ourselves + if ((currentUserName.equals(authorityName) == true) || + (permissionService.hasPermission(siteNodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)) + { + // Run as system user + AuthenticationUtil.runAs( + new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Remove the user from the current permission + // group + String currentGroup = getSiteRoleGroup(shortName, role, true); + authorityService.removeAuthority(currentGroup, authorityName); + + return null; + } + }, AuthenticationUtil.SYSTEM_USER_NAME); + + // Raise events + AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName); + if (authorityType == AuthorityType.USER) + { + activityService.postActivity( + ActivityType.SITE_USER_REMOVED, shortName, + ACTIVITY_TOOL, getActivityUserData(authorityName, "")); + } + else if (authorityType == AuthorityType.GROUP) + { + activityService.postActivity( + ActivityType.SITE_GROUP_REMOVED, shortName, + ACTIVITY_TOOL, getActivityGroupData(authorityName, "")); + } + } + else + { + // Throw an exception + throw new SiteServiceException(MSG_CAN_NOT_REMOVE_MSHIP, new Object[]{shortName}); + } + } + else + { + // Throw an exception + throw new SiteServiceException(MSG_CAN_NOT_REMOVE_MSHIP, new Object[]{shortName}); + } + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#setMembership(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void setMembership(final String shortName, + final String authorityName, + final String role) + { + final NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); + } + + // Get the user's current role + final String currentRole = getMembersRole(shortName, authorityName); + + // Do nothing if the role of the user is not being changed + if (currentRole == null || role.equals(currentRole) == false) + { + // TODO if this is the only site manager do not down grade their + // permissions + + // Get the visibility of the site + SiteVisibility visibility = getSiteVisibility(siteNodeRef); + + // If we are ... + // -- the current user has change permissions rights on the site + // or we are ... + // -- referring to a public site and + // -- the role being set is consumer and + // -- the user being added is ourselves and + // -- the member does not already have permissions + // ... then we can set the permissions as system user + final String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); + if ((permissionService.hasPermission(siteNodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) || + (SiteVisibility.PUBLIC.equals(visibility) == true && + role.equals(SiteModel.SITE_CONSUMER) == true && + authorityName.equals(currentUserName) == true && + currentRole == null)) + { + // Check that we are not about to remove the last site manager + checkLastManagerRemoval(shortName, authorityName, currentRole); + + // Run as system user + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + if (currentRole != null) + { + // Remove the user from the current + // permission group + String currentGroup = getSiteRoleGroup(shortName, currentRole, true); + authorityService.removeAuthority(currentGroup, authorityName); + } + + // Add the user to the new permission group + String newGroup = getSiteRoleGroup(shortName, role, true); + authorityService.addAuthority(newGroup, authorityName); + + return null; + } + + }, AuthenticationUtil.SYSTEM_USER_NAME); + + if (currentRole == null) + { + AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName); + if (authorityType == AuthorityType.USER) + { + activityService.postActivity( + ActivityType.SITE_USER_JOINED, shortName, + ACTIVITY_TOOL, getActivityUserData(authorityName, role)); + } + else if (authorityType == AuthorityType.GROUP) + { + activityService.postActivity( + ActivityType.SITE_GROUP_ADDED, shortName, + ACTIVITY_TOOL, getActivityGroupData(authorityName, role)); + } + } + else + { + AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName); + if (authorityType == AuthorityType.USER) + { + activityService.postActivity( + ActivityType.SITE_USER_ROLE_UPDATE, shortName, + ACTIVITY_TOOL, getActivityUserData(authorityName, role)); + } + else if (authorityType == AuthorityType.GROUP) + { + activityService.postActivity( + ActivityType.SITE_GROUP_ROLE_UPDATE, shortName, + ACTIVITY_TOOL, getActivityGroupData(authorityName, role)); + } + } + } + else + { + // Raise a permission exception + throw new SiteServiceException(MSG_CAN_NOT_CHANGE_MSHIP, new Object[]{shortName}); + } + } + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#createContainer(java.lang.String, + * java.lang.String, org.alfresco.service.namespace.QName, + * java.util.Map) + */ + public NodeRef createContainer(String shortName, + String componentId, + QName containerType, + Map containerProperties) + { + // Check for the component id + ParameterCheck.mandatoryString("componentId", componentId); + + // retrieve site + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); + } + + // Update the isPublic flag + SiteVisibility siteVisibility = getSiteVisibility(siteNodeRef); + + // retrieve component folder within site + NodeRef containerNodeRef = null; + try + { + containerNodeRef = findContainer(siteNodeRef, componentId); + } + catch (FileNotFoundException e) + { + //NOOP + } + + // create the container node reference + if (containerNodeRef == null) + { + if (containerType == null) + { + containerType = ContentModel.TYPE_FOLDER; + } + + // create component folder + FileInfo fileInfo = fileFolderService.create(siteNodeRef, + componentId, containerType); + + // Get the created container + containerNodeRef = fileInfo.getNodeRef(); + + // Set the properties if they have been provided + if (containerProperties != null) + { + Map props = this.nodeService + .getProperties(containerNodeRef); + props.putAll(containerProperties); + this.nodeService.setProperties(containerNodeRef, props); + } + + // Add the container aspect + Map aspectProps = new HashMap(1, 1.0f); + aspectProps.put(SiteModel.PROP_COMPONENT_ID, componentId); + this.nodeService.addAspect(containerNodeRef, ASPECT_SITE_CONTAINER, + aspectProps); + + // Set permissions on the container + if(SiteVisibility.MODERATED.equals(siteVisibility)) + { + setModeratedPermissions(shortName, containerNodeRef); + } + + // Make the container a tag scope + this.taggingService.addTagScope(containerNodeRef); + } + + return containerNodeRef; + } + + /** * This method recursively cleans the site permissions on the specified NodeRef and all its primary * descendants. This consists of *
    @@ -1742,220 +1740,221 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * Moderated sites have separate ACLs on each component and don't inherit from the - * site which has consumer role for everyone. - */ - private void setModeratedPermissions(String shortName, NodeRef containerNodeRef) - { - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[] { shortName }); - } - - QName siteType = nodeService.getType(siteNodeRef); - Set permissions = permissionService.getSettablePermissions(siteType); - for (String permission : permissions) - { - String permissionGroup = getSiteRoleGroup(shortName, permission, true); - // Assign the group the relevant permission on the site - permissionService.setPermission(containerNodeRef, permissionGroup, permission, true); - } - permissionService.setPermission(containerNodeRef, - PermissionService.ALL_AUTHORITIES, - PermissionService.READ_PERMISSIONS, true); - - this.permissionService.setInheritParentPermissions(containerNodeRef, false); - } - - - /** - * @see org.alfresco.service.cmr.site.SiteService#getContainer(java.lang.String) - */ - public NodeRef getContainer(String shortName, String componentId) - { - ParameterCheck.mandatoryString("componentId", componentId); - - // retrieve site - NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); - } - - // retrieve component folder within site - // NOTE: component id is used for folder name - NodeRef containerNodeRef = null; - try - { - containerNodeRef = findContainer(siteNodeRef, componentId); - } - catch (FileNotFoundException e) - { - } - - return containerNodeRef; - } - - /** - * @see org.alfresco.service.cmr.site.SiteService#hasContainer(java.lang.String) - */ - public boolean hasContainer(final String shortName, final String componentId) - { - ParameterCheck.mandatoryString("componentId", componentId); - - // retrieve site - final NodeRef siteNodeRef = getSiteNodeRef(shortName); - if (siteNodeRef == null) - { - throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); - } - - // retrieve component folder within site - // NOTE: component id is used for folder name - boolean hasContainer = false; - - NodeRef containerRef = AuthenticationUtil.runAs(new RunAsWork() - { - public NodeRef doWork() throws Exception - { - return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() - { - public NodeRef execute() throws Exception - { - try - { - return findContainer(siteNodeRef, componentId); - } - catch (FileNotFoundException e) - { - return null; - } - } - }, true); - } - }, AuthenticationUtil.getSystemUserName()); - - if(containerRef != null) - { - hasContainer = true; - } - - return hasContainer; - } - - /** - * Locate site "container" folder for component - * - * @param siteNodeRef - * site - * @param componentId - * component id - * @return "container" node ref, if it exists - * @throws FileNotFoundException - */ - private NodeRef findContainer(NodeRef siteNodeRef, String componentId) - throws FileNotFoundException - { - List paths = new ArrayList(1); - paths.add(componentId); - FileInfo fileInfo = fileFolderService.resolveNamePath(siteNodeRef, - paths); - if (!fileInfo.isFolder()) - { - throw new SiteServiceException(MSG_SITE_CONTAINER_NOT_FOLDER, new Object[]{fileInfo.getName()}); - } - return fileInfo.getNodeRef(); - } - - /** - * Helper method to get the activity data for a user - * - * @param userName user name - * @param role role - * @return - */ - private String getActivityUserData(String userName, String role) - { - String memberFN = ""; - String memberLN = ""; - NodeRef person = personService.getPerson(userName); - if (person != null) - { - memberFN = (String) nodeService.getProperty(person, - ContentModel.PROP_FIRSTNAME); - memberLN = (String) nodeService.getProperty(person, - ContentModel.PROP_LASTNAME); - } - - try - { - JSONObject activityData = new JSONObject(); - activityData.put("role", role); - activityData.put("memberUserName", userName); - activityData.put("memberFirstName", memberFN); - activityData.put("memberLastName", memberLN); - activityData.put("title", (memberFN + " " + memberLN + " (" - + userName + ")").trim()); - return activityData.toString(); - } catch (JSONException je) - { - // log error, subsume exception - logger.error("Failed to get activity data: " + je); - return ""; - } - } - - /** - * Helper method to get the activity data for a group - * - * @param groupName user name - * @param role role - * @return Activity data in JSON format - */ - private String getActivityGroupData(String groupName, String role) - { - try - { - JSONObject activityData = new JSONObject(); - activityData.put("role", role); - activityData.put("groupName", groupName); - - return activityData.toString(); - } - catch (JSONException je) - { - // log error, subsume exception - logger.error("Failed to get activity data: " + je); - return ""; - } - } - - /** - * Helper to check that we are not removing the last Site Manager from a site - * - * @param shortName - * @param authorityName - * @param role - */ - private void checkLastManagerRemoval(final String shortName, final String authorityName, final String role) - { - // Check that we are not about to remove the last site manager - if (SiteModel.SITE_MANAGER.equals(role) == true) - { - String mgrGroup = getSiteRoleGroup(shortName, SITE_MANAGER, true); - Set siteUserMangers = this.authorityService.getContainedAuthorities( - AuthorityType.USER, mgrGroup, true); - if (siteUserMangers.size() <= 1) - { - Set siteGroupManagers = this.authorityService.getContainedAuthorities( - AuthorityType.GROUP, mgrGroup, true); - - if (siteUserMangers.size() + siteGroupManagers.size() == 1) - { - throw new SiteServiceException(MSG_DO_NOT_CHANGE_MGR, new Object[] {authorityName}); - } - } - } - } -} + * Moderated sites have separate ACLs on each component and don't inherit from the + * site which has consumer role for everyone. + */ + private void setModeratedPermissions(String shortName, NodeRef containerNodeRef) + { + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[] { shortName }); + } + + QName siteType = nodeService.getType(siteNodeRef); + Set permissions = permissionService.getSettablePermissions(siteType); + for (String permission : permissions) + { + String permissionGroup = getSiteRoleGroup(shortName, permission, true); + // Assign the group the relevant permission on the site + permissionService.setPermission(containerNodeRef, permissionGroup, permission, true); + } + permissionService.setPermission(containerNodeRef, + PermissionService.ALL_AUTHORITIES, + PermissionService.READ_PERMISSIONS, true); + + this.permissionService.setInheritParentPermissions(containerNodeRef, false); + } + + + /** + * @see org.alfresco.service.cmr.site.SiteService#getContainer(java.lang.String) + */ + public NodeRef getContainer(String shortName, String componentId) + { + ParameterCheck.mandatoryString("componentId", componentId); + + // retrieve site + NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); + } + + // retrieve component folder within site + // NOTE: component id is used for folder name + NodeRef containerNodeRef = null; + try + { + containerNodeRef = findContainer(siteNodeRef, componentId); + } + catch (FileNotFoundException e) + { + //NOOP + } + + return containerNodeRef; + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#hasContainer(java.lang.String) + */ + public boolean hasContainer(final String shortName, final String componentId) + { + ParameterCheck.mandatoryString("componentId", componentId); + + // retrieve site + final NodeRef siteNodeRef = getSiteNodeRef(shortName); + if (siteNodeRef == null) + { + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); + } + + // retrieve component folder within site + // NOTE: component id is used for folder name + boolean hasContainer = false; + + NodeRef containerRef = AuthenticationUtil.runAs(new RunAsWork() + { + public NodeRef doWork() throws Exception + { + return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Exception + { + try + { + return findContainer(siteNodeRef, componentId); + } + catch (FileNotFoundException e) + { + return null; + } + } + }, true); + } + }, AuthenticationUtil.getSystemUserName()); + + if(containerRef != null) + { + hasContainer = true; + } + + return hasContainer; + } + + /** + * Locate site "container" folder for component + * + * @param siteNodeRef + * site + * @param componentId + * component id + * @return "container" node ref, if it exists + * @throws FileNotFoundException + */ + private NodeRef findContainer(NodeRef siteNodeRef, String componentId) + throws FileNotFoundException + { + List paths = new ArrayList(1); + paths.add(componentId); + FileInfo fileInfo = fileFolderService.resolveNamePath(siteNodeRef, + paths); + if (!fileInfo.isFolder()) + { + throw new SiteServiceException(MSG_SITE_CONTAINER_NOT_FOLDER, new Object[]{fileInfo.getName()}); + } + return fileInfo.getNodeRef(); + } + + /** + * Helper method to get the activity data for a user + * + * @param userName user name + * @param role role + * @return + */ + private String getActivityUserData(String userName, String role) + { + String memberFN = ""; + String memberLN = ""; + NodeRef person = personService.getPerson(userName); + if (person != null) + { + memberFN = (String) nodeService.getProperty(person, + ContentModel.PROP_FIRSTNAME); + memberLN = (String) nodeService.getProperty(person, + ContentModel.PROP_LASTNAME); + } + + try + { + JSONObject activityData = new JSONObject(); + activityData.put("role", role); + activityData.put("memberUserName", userName); + activityData.put("memberFirstName", memberFN); + activityData.put("memberLastName", memberLN); + activityData.put("title", (memberFN + " " + memberLN + " (" + + userName + ")").trim()); + return activityData.toString(); + } catch (JSONException je) + { + // log error, subsume exception + logger.error("Failed to get activity data: " + je); + return ""; + } + } + + /** + * Helper method to get the activity data for a group + * + * @param groupName user name + * @param role role + * @return Activity data in JSON format + */ + private String getActivityGroupData(String groupName, String role) + { + try + { + JSONObject activityData = new JSONObject(); + activityData.put("role", role); + activityData.put("groupName", groupName); + + return activityData.toString(); + } + catch (JSONException je) + { + // log error, subsume exception + logger.error("Failed to get activity data: " + je); + return ""; + } + } + + /** + * Helper to check that we are not removing the last Site Manager from a site + * + * @param shortName + * @param authorityName + * @param role + */ + private void checkLastManagerRemoval(final String shortName, final String authorityName, final String role) + { + // Check that we are not about to remove the last site manager + if (SiteModel.SITE_MANAGER.equals(role) == true) + { + String mgrGroup = getSiteRoleGroup(shortName, SITE_MANAGER, true); + Set siteUserMangers = this.authorityService.getContainedAuthorities( + AuthorityType.USER, mgrGroup, true); + if (siteUserMangers.size() <= 1) + { + Set siteGroupManagers = this.authorityService.getContainedAuthorities( + AuthorityType.GROUP, mgrGroup, true); + + if (siteUserMangers.size() + siteGroupManagers.size() == 1) + { + throw new SiteServiceException(MSG_DO_NOT_CHANGE_MGR, new Object[] {authorityName}); + } + } + } + } +} diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java index 356f947704..13636ac81b 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java @@ -41,6 +41,7 @@ import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authority.UnknownAuthorityException; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.model.FileFolderService; @@ -1353,6 +1354,17 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest // From sub group four assertEquals(SiteModel.SITE_MANAGER, this.siteService.getMembersRole("testMembership", USER_FOUR)); + // Set a membership with an illegal role. See ALF-619. + // I'm checking that the exception type thrown is what it should be. + try + { + this.siteService.setMembership("testMembership", this.groupThree, "rubbish"); + } + catch (UnknownAuthorityException expected) + { + return; + } + fail("Expected exception not thrown."); } /** diff --git a/source/java/org/alfresco/repo/site/SiteServiceInternal.java b/source/java/org/alfresco/repo/site/SiteServiceInternal.java new file mode 100644 index 0000000000..8f97ae7cfe --- /dev/null +++ b/source/java/org/alfresco/repo/site/SiteServiceInternal.java @@ -0,0 +1,40 @@ +/* + * 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 . + */ + +package org.alfresco.repo.site; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteService; + +/** + * Internal interface used to expose the getSiteRoot() method without polluting the public interface {@link SiteService}. + * @author Nick Smith + * @since 4.0 + * + */ +public interface SiteServiceInternal extends SiteService +{ + + /** + * Get the node reference that is the site root + * + * @return NodeRef node reference + */ + NodeRef getSiteRoot(); +} diff --git a/source/java/org/alfresco/repo/version/NodeServiceImpl.java b/source/java/org/alfresco/repo/version/NodeServiceImpl.java index a673eb3f24..9fd7ef0b7a 100644 --- a/source/java/org/alfresco/repo/version/NodeServiceImpl.java +++ b/source/java/org/alfresco/repo/version/NodeServiceImpl.java @@ -260,11 +260,21 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ + @Override public boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef) { throw new UnsupportedOperationException(MSG_UNSUPPORTED); } + /** + * @throws UnsupportedOperationException always + */ + @Override + public boolean removeSecondaryChildAssociation(ChildAssociationRef childAssocRef) + { + throw new UnsupportedOperationException(MSG_UNSUPPORTED); + } + /** * @throws UnsupportedOperationException always */ diff --git a/source/java/org/alfresco/service/cmr/lock/LockService.java b/source/java/org/alfresco/service/cmr/lock/LockService.java index 32a0bb5552..0162c92c8c 100644 --- a/source/java/org/alfresco/service/cmr/lock/LockService.java +++ b/source/java/org/alfresco/service/cmr/lock/LockService.java @@ -234,13 +234,16 @@ public interface LockService public LockType getLockType(NodeRef nodeRef); /** - * Checks to see if the node is locked or not. Gets the user reference from the current - * session. + * Checks to see if the current user has access to the specified node. + *

    + * If the node is locked by another user then a NodeLockedException is thrown. + *

    + * Gets the user reference from the current session. * * @param nodeRef the node reference * * @throws NodeLockedException - * thrown if the node is locked. This is based on the lock status of the lock, + * thrown if the node is locked by someone else. This is based on the lock status of the lock, * the user ref and the lock type. */ @Auditable(parameters = {"nodeRef"}) diff --git a/source/java/org/alfresco/service/cmr/repository/NodeLocator.java b/source/java/org/alfresco/service/cmr/repository/NodeLocator.java new file mode 100644 index 0000000000..758969631a --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/NodeLocator.java @@ -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 . + */ + +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.cmr.action.ParameterDefinition; + +/** + * A strategy for locating a {@link NodeRef} in the repository, given a source node and an arbitrary set of parameters. + * + * @author Nick Smith + * @since 4.0 + * + */ +public interface NodeLocator +{ + /** + * Finds a {@link NodeRef} given a starting {@link NodeRef} and a + * {@link Map} of parameters. + * Returns null if the specified node could not be found. + * + * @param sourceNode the starting point for locating a new node. The source node. Can be null. + * @param params an arbitrary {@link Map} of parameters.Can be null. + * @return the node to be found or null. + */ + NodeRef getNode(NodeRef source, Map params); + + /** + * A list containing the parmameter defintions for this {@link NodeLocator}. + * + * @return a list of parameter definitions + */ + public List getParameterDefinitions(); +} diff --git a/source/java/org/alfresco/service/cmr/repository/NodeLocatorService.java b/source/java/org/alfresco/service/cmr/repository/NodeLocatorService.java new file mode 100644 index 0000000000..c418d709b4 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/NodeLocatorService.java @@ -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 . + */ + +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.util.Map; + +/** + * This service is responsible for locating {@link NodeRef}s in the repository using {@link NodeLocator} strategies. + * + * @author Nick Smith + * @since 4.0 + * + */ +public interface NodeLocatorService +{ + /** + * Locates and returns a {@link NodeRef} using the specified {@link NodeLocator}. + * + * @param locatorName the name of the {@link NodeLocator} to use. + * @param source the source node. Can be null. + * @param params An arbitrary set of parameters. Can be null. + * @return the node to be found or null. + */ + NodeRef getNode(String locatorName, NodeRef source, Map params); + + void register(String locatorName, NodeLocator locator); +} diff --git a/source/java/org/alfresco/service/cmr/site/SiteService.java b/source/java/org/alfresco/service/cmr/site/SiteService.java index 9af85bbdd1..3b19ec302c 100644 --- a/source/java/org/alfresco/service/cmr/site/SiteService.java +++ b/source/java/org/alfresco/service/cmr/site/SiteService.java @@ -22,6 +22,7 @@ import java.io.Serializable; import java.util.List; import java.util.Map; +import org.alfresco.repo.security.authority.UnknownAuthorityException; import org.alfresco.service.Auditable; import org.alfresco.service.NotAuditable; import org.alfresco.service.PublicService; @@ -38,6 +39,8 @@ import org.alfresco.service.namespace.QName; @PublicService public interface SiteService { + static String DOCUMENT_LIBRARY = "documentLibrary"; + /** * Create a new site. * @@ -197,6 +200,7 @@ public interface SiteService * @param shortName site short name * @param authorityName authority name (so if it's a group then its prefixed with 'GROUP_') * @param role site role + * @throws UnknownAuthorityException if the site role is not supported. */ @Auditable(parameters = {"shortName", "authorityName", "role"}) void setMembership(String shortName, String authorityName, String role); diff --git a/source/test-resources/test-nodeLocatorServiceImpl-context.xml b/source/test-resources/test-nodeLocatorServiceImpl-context.xml new file mode 100644 index 0000000000..db4be946b4 --- /dev/null +++ b/source/test-resources/test-nodeLocatorServiceImpl-context.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file