diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml
index 422ea4a933..f28cd1b9db 100644
--- a/config/alfresco/bootstrap-context.xml
+++ b/config/alfresco/bootstrap-context.xml
@@ -141,6 +141,7 @@
+
diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index ca3a2d06f4..937c81c92a 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -1307,6 +1307,10 @@
+
+
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 7fe6f2a93c..7e4b49fad3 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
@@ -23,6 +23,15 @@ CREATE TABLE alf_applied_patch
PRIMARY KEY (id)
) ENGINE=InnoDB;
+CREATE TABLE alf_locale
+(
+ id BIGINT NOT NULL AUTO_INCREMENT,
+ version BIGINT NOT NULL,
+ locale_str VARCHAR(20) NOT NULL,
+ PRIMARY KEY (id),
+ UNIQUE KEY locale_str (locale_str)
+) ENGINE=InnoDB;
+
CREATE TABLE alf_namespace
(
id BIGINT NOT NULL AUTO_INCREMENT,
@@ -196,6 +205,7 @@ CREATE TABLE alf_node
transaction_id BIGINT NOT NULL,
node_deleted bit NOT NULL,
type_qname_id BIGINT NOT NULL,
+ locale_id BIGINT NOT NULL,
acl_id BIGINT,
audit_creator VARCHAR(255),
audit_created VARCHAR(30),
@@ -209,10 +219,12 @@ CREATE TABLE alf_node
KEY fk_alf_node_txn (transaction_id),
KEY fk_alf_node_store (store_id),
KEY fk_alf_node_tqn (type_qname_id),
+ KEY fk_alf_node_loc (locale_id),
CONSTRAINT fk_alf_node_acl FOREIGN KEY (acl_id) REFERENCES alf_access_control_list (id),
CONSTRAINT fk_alf_node_store FOREIGN KEY (store_id) REFERENCES alf_store (id),
CONSTRAINT fk_alf_node_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id),
- CONSTRAINT fk_alf_node_txn FOREIGN KEY (transaction_id) REFERENCES alf_transaction (id)
+ CONSTRAINT fk_alf_node_txn FOREIGN KEY (transaction_id) REFERENCES alf_transaction (id),
+ CONSTRAINT fk_alf_node_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id)
) ENGINE=InnoDB;
ALTER TABLE alf_store ADD INDEX fk_alf_store_root (root_node_id), ADD CONSTRAINT fk_alf_store_root FOREIGN KEY (root_node_id) REFERENCES alf_node (id);
@@ -245,15 +257,6 @@ CREATE TABLE alf_child_assoc
CONSTRAINT fk_alf_cass_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id)
) ENGINE=InnoDB;
-CREATE TABLE alf_locale
-(
- id BIGINT NOT NULL AUTO_INCREMENT,
- version BIGINT NOT NULL,
- locale_str VARCHAR(20) NOT NULL,
- PRIMARY KEY (id),
- UNIQUE KEY locale_str (locale_str)
-) 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 5b04d17daf..0c2ca1fc38 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
@@ -23,6 +23,16 @@ CREATE TABLE alf_applied_patch
PRIMARY KEY (id)
);
+CREATE SEQUENCE alf_locale_seq START WITH 1 INCREMENT BY 1;
+CREATE TABLE alf_locale
+(
+ id INT8 NOT NULL,
+ version INT8 NOT NULL,
+ locale_str VARCHAR(20) NOT NULL,
+ PRIMARY KEY (id)
+);
+CREATE UNIQUE INDEX locale_str ON alf_locale (locale_str);
+
CREATE SEQUENCE alf_namespace_seq START WITH 1 INCREMENT BY 1;
CREATE TABLE alf_namespace
(
@@ -210,6 +220,7 @@ CREATE TABLE alf_node
transaction_id INT8 NOT NULL,
node_deleted BOOL NOT NULL,
type_qname_id INT8 NOT NULL,
+ locale_id INT8 NOT NULL,
acl_id INT8,
audit_creator VARCHAR(255),
audit_created VARCHAR(30),
@@ -220,7 +231,8 @@ CREATE TABLE alf_node
CONSTRAINT fk_alf_node_acl FOREIGN KEY (acl_id) REFERENCES alf_access_control_list (id),
CONSTRAINT fk_alf_node_store FOREIGN KEY (store_id) REFERENCES alf_store (id),
CONSTRAINT fk_alf_node_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id),
- CONSTRAINT fk_alf_node_txn FOREIGN KEY (transaction_id) REFERENCES alf_transaction (id)
+ CONSTRAINT fk_alf_node_txn FOREIGN KEY (transaction_id) REFERENCES alf_transaction (id),
+ CONSTRAINT fk_alf_node_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id)
);
CREATE UNIQUE INDEX store_id ON alf_node (store_id, uuid);
CREATE INDEX idx_alf_node_del ON alf_node (node_deleted);
@@ -228,6 +240,7 @@ CREATE INDEX fk_alf_node_acl ON alf_node (acl_id);
CREATE INDEX fk_alf_node_txn ON alf_node (transaction_id);
CREATE INDEX fk_alf_node_store ON alf_node (store_id);
CREATE INDEX fk_alf_node_tqn ON alf_node (type_qname_id);
+CREATE INDEX fk_alf_node_loc ON alf_node (locale_id);
CREATE INDEX fk_alf_store_root ON alf_store (root_node_id);
ALTER TABLE alf_store ADD CONSTRAINT fk_alf_store_root FOREIGN KEY (root_node_id) REFERENCES alf_node (id);
@@ -261,16 +274,6 @@ CREATE INDEX fk_alf_cass_qnns ON alf_child_assoc (qname_ns_id);
CREATE INDEX idx_alf_cass_qncrc ON alf_child_assoc (qname_crc, type_qname_id, parent_node_id);
CREATE INDEX idx_alf_cass_pri ON alf_child_assoc (parent_node_id, is_primary, child_node_id);
-CREATE SEQUENCE alf_locale_seq START WITH 1 INCREMENT BY 1;
-CREATE TABLE alf_locale
-(
- id INT8 NOT NULL,
- version INT8 NOT NULL,
- locale_str VARCHAR(20) NOT NULL,
- PRIMARY KEY (id)
-);
-CREATE UNIQUE INDEX locale_str ON alf_locale (locale_str);
-
CREATE TABLE alf_node_aspects
(
node_id INT8 NOT NULL,
diff --git a/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.MySQLInnoDBDialect/Node-Locale.sql b/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.MySQLInnoDBDialect/Node-Locale.sql
new file mode 100644
index 0000000000..b222e08e8a
--- /dev/null
+++ b/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.MySQLInnoDBDialect/Node-Locale.sql
@@ -0,0 +1,30 @@
+--
+-- Title: Add 'locale_id' column to 'alf_node'
+-- Database: MySQL
+-- Since: V4.0 Schema 5010
+-- Author: Derek Hulley
+--
+-- Please contact support@alfresco.com if you need assistance with the upgrade.
+--
+
+--ASSIGN:def_locale_id=id
+SELECT id FROM alf_locale WHERE locale_str = '.default';
+
+-- Add the column, using a default to fill
+ALTER TABLE alf_node
+ ADD COLUMN locale_id INT8 NOT NULL DEFAULT ${def_locale_id} AFTER type_qname_id,
+ ADD KEY fk_alf_node_loc (locale_id),
+ ADD CONSTRAINT fk_alf_node_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id)
+;
+
+--
+-- Record script finish
+--
+DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.0-Node-Locale';
+INSERT INTO alf_applied_patch
+ (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report)
+ VALUES
+ (
+ 'patch.db-V4.0-Node-Locale', 'Manually executed script upgrade V4.0: Add locale_id column to alf_node',
+ 0, 5009, -1, 5010, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed'
+ );
\ No newline at end of file
diff --git a/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.PostgreSQLDialect/Node-Locale.sql b/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.PostgreSQLDialect/Node-Locale.sql
new file mode 100644
index 0000000000..a617442cd1
--- /dev/null
+++ b/config/alfresco/dbscripts/upgrade/4.0/org.hibernate.dialect.PostgreSQLDialect/Node-Locale.sql
@@ -0,0 +1,32 @@
+--
+-- Title: Add 'locale_id' column to 'alf_node'
+-- Database: PostgreSQL
+-- Since: V4.0 Schema 5010
+-- Author: Derek Hulley
+--
+-- Please contact support@alfresco.com if you need assistance with the upgrade.
+--
+
+--ASSIGN:def_locale_id=id
+SELECT id FROM alf_locale WHERE locale_str = '.default';
+
+-- Add the column, using a default to fill
+ALTER TABLE alf_node
+ ADD COLUMN locale_id INT8 NOT NULL DEFAULT ${def_locale_id}
+;
+ALTER TABLE alf_node
+ ADD CONSTRAINT fk_alf_node_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id)
+;
+CREATE INDEX fk_alf_node_loc ON alf_node (locale_id);
+
+--
+-- Record script finish
+--
+DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.0-Node-Locale';
+INSERT INTO alf_applied_patch
+ (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report)
+ VALUES
+ (
+ 'patch.db-V4.0-Node-Locale', 'Manually executed script upgrade V4.0: Add locale_id column to alf_node',
+ 0, 5009, -1, 5010, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed'
+ );
\ No newline at end of file
diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
index e36c46f0a8..c3472c9710 100644
--- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
@@ -64,6 +64,7 @@
+
@@ -229,7 +230,7 @@
insert into alf_node
(
- version, store_id, uuid, type_qname_id, acl_id, node_deleted, transaction_id
+ version, store_id, uuid, type_qname_id, locale_id, acl_id, node_deleted, transaction_id
, audit_creator, audit_created,
audit_modifier, audit_modified,
@@ -238,7 +239,7 @@
)
values
(
- #{version}, #{store.id}, #{uuid}, #{typeQNameId}, #{aclId,jdbcType=BIGINT}, #{deleted}, #{transaction.id}
+ #{version}, #{store.id}, #{uuid}, #{typeQNameId}, #{localeId}, #{aclId,jdbcType=BIGINT}, #{deleted}, #{transaction.id}
, #{auditableProperties.auditCreator,jdbcType=VARCHAR}, #{auditableProperties.auditCreated,jdbcType=VARCHAR},
#{auditableProperties.auditModifier,jdbcType=VARCHAR}, #{auditableProperties.auditModified,jdbcType=VARCHAR},
@@ -250,7 +251,7 @@
insert into alf_node
(
- id, version, store_id, uuid, type_qname_id, acl_id, node_deleted, transaction_id
+ id, version, store_id, uuid, type_qname_id, locale_id, acl_id, node_deleted, transaction_id
, audit_creator, audit_created,
audit_modifier, audit_modified,
@@ -259,7 +260,7 @@
)
values
(
- #{id}, #{version}, #{store.id}, #{uuid}, #{typeQNameId}, #{aclId,jdbcType=BIGINT}, #{deleted}, #{transaction.id}
+ #{id}, #{version}, #{store.id}, #{uuid}, #{typeQNameId}, #{localeId}, #{aclId,jdbcType=BIGINT}, #{deleted}, #{transaction.id}
, #{auditableProperties.auditCreator,jdbcType=VARCHAR}, #{auditableProperties.auditCreated,jdbcType=VARCHAR},
#{auditableProperties.auditModifier,jdbcType=VARCHAR}, #{auditableProperties.auditModified,jdbcType=VARCHAR},
@@ -327,7 +328,6 @@
?, ?,
?, ?, ?, ?, ?, ?
)
-
@@ -369,6 +369,7 @@
, store_id = #{store.id}
, uuid = #{uuid}
, type_qname_id = #{typeQNameId}
+ , locale_id = #{localeId}
, acl_id = #{aclId,jdbcType=BIGINT}
, node_deleted = #{deleted}
, transaction_id = #{transaction.id}
@@ -604,6 +605,7 @@
store.identifier as identifier,
node.uuid as uuid,
node.type_qname_id as type_qname_id,
+ node.locale_id as locale_id,
node.acl_id as acl_id,
node.node_deleted as node_deleted,
txn.id as txn_id,
diff --git a/config/alfresco/model/publishingModel.xml b/config/alfresco/model/publishingModel.xml
index e8223ded08..063431a793 100644
--- a/config/alfresco/model/publishingModel.xml
+++ b/config/alfresco/model/publishingModel.xml
@@ -83,7 +83,7 @@
sys:base
-
+
false
false
@@ -171,6 +171,14 @@
Comment for the event
d:text
+
+ Channel to publish to
+ d:text
+
+
+ The Id of the associated Publishing Event Workflow Instance
+ d:text
+
diff --git a/config/alfresco/model/systemModel.xml b/config/alfresco/model/systemModel.xml
index 3e2792aaf7..81846d38ee 100644
--- a/config/alfresco/model/systemModel.xml
+++ b/config/alfresco/model/systemModel.xml
@@ -26,6 +26,7 @@
Base
sys:referenceable
+ sys:localized
diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml
index 56d8e8b214..72db6397cb 100644
--- a/config/alfresco/patch/patch-services-context.xml
+++ b/config/alfresco/patch/patch-services-context.xml
@@ -2860,4 +2860,15 @@
+
+
+
+
+
+
+
+ classpath:alfresco/dbscripts/upgrade/4.0/${db.script.dialect}/Node-Locale.sql
+
+
+
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index f30ba929a4..9d859b5c64 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -149,7 +149,7 @@ system.acl.maxPermissionCheckTimeMillis=10000
# The maximum number of search results to perform permission checks against
system.acl.maxPermissionChecks=1000
-# The maximum number of filefolder list results (note: also affected by system.acl.maxPermissionCheckTimeMillis)
+# The maximum number of filefolder list results
system.filefolderservice.defaultListMaxResults=5000
diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties
index c1358868da..875bcafa63 100644
--- a/config/alfresco/version.properties
+++ b/config/alfresco/version.properties
@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
-version.schema=5009
+version.schema=5010
diff --git a/config/alfresco/web-publishing-context.xml b/config/alfresco/web-publishing-context.xml
index e09611cc70..723742119c 100644
--- a/config/alfresco/web-publishing-context.xml
+++ b/config/alfresco/web-publishing-context.xml
@@ -48,7 +48,7 @@
-
+
@@ -61,6 +61,8 @@
+
+
@@ -81,10 +83,13 @@
+
+
+
diff --git a/config/test/alfresco/test-web-publishing-context.xml b/config/test/alfresco/test-web-publishing-context.xml
index b6bfa5e32b..4108c9140b 100644
--- a/config/test/alfresco/test-web-publishing-context.xml
+++ b/config/test/alfresco/test-web-publishing-context.xml
@@ -45,10 +45,15 @@
-
+
+
+
+
+
+
diff --git a/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java b/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java
index 6f08d11a9b..2002f76892 100644
--- a/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java
+++ b/source/java/org/alfresco/jcr/dictionary/DataTypeMap.java
@@ -56,6 +56,7 @@ public class DataTypeMap
dataTypeToPropertyType.put(DataTypeDefinition.NODE_REF, PropertyType.REFERENCE);
dataTypeToPropertyType.put(DataTypeDefinition.PATH, PropertyType.PATH);
dataTypeToPropertyType.put(DataTypeDefinition.ANY, PropertyType.UNDEFINED);
+ dataTypeToPropertyType.put(DataTypeDefinition.LOCALE, PropertyType.STRING);
}
/** Map of JCR Property Type to Alfresco Data Type */
diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisService.java b/source/java/org/alfresco/opencmis/AlfrescoCmisService.java
index f133f64b75..8ca4f111b4 100644
--- a/source/java/org/alfresco/opencmis/AlfrescoCmisService.java
+++ b/source/java/org/alfresco/opencmis/AlfrescoCmisService.java
@@ -597,11 +597,10 @@ public class AlfrescoCmisService extends AbstractCmisService
}
}
- if (pageOfNodeInfos.hasMoreItems() != null)
- {
- result.setHasMoreItems(pageOfNodeInfos.hasMoreItems());
- }
+ /// has more ?
+ result.setHasMoreItems(pageOfNodeInfos.hasMoreItems());
+ // total count ?
Pair totalCounts = pageOfNodeInfos.getTotalResultCount();
if (totalCounts != null)
{
diff --git a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerScalabilityRunner.java b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerScalabilityRunner.java
index 4562c220b3..d748b1dfac 100644
--- a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerScalabilityRunner.java
+++ b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerScalabilityRunner.java
@@ -47,6 +47,7 @@ import org.alfresco.util.TempFileProvider;
import org.alfresco.util.VmShutdownListener;
import org.apache.commons.lang.mutable.MutableInt;
import org.springframework.context.ApplicationContext;
+import org.springframework.extensions.surf.util.I18NUtil;
/**
* Loads the repository up with orphaned content and then runs the cleaner.
@@ -266,6 +267,7 @@ public class ContentStoreCleanerScalabilityRunner extends Repository
storeRef,
null,
ContentModel.TYPE_CONTENT,
+ I18NUtil.getLocale(),
null,
null);
Long nodeId = assoc.getChildNode().getId();
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index 2daa06bd46..44036d2da7 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -29,6 +29,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
@@ -77,6 +78,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.ReadOnlyServerException;
import org.alfresco.service.transaction.TransactionService;
@@ -655,7 +657,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Long aclId = aclDAO.createAccessControlList();
// Create a root node
- NodeEntity rootNode = newNodeImpl(store, null, ContentModel.TYPE_STOREROOT, aclId, false, null);
+ NodeEntity rootNode = newNodeImpl(store, null, ContentModel.TYPE_STOREROOT, null, aclId, false, null);
Long rootNodeId = rootNode.getId();
addNodeAspects(rootNodeId, Collections.singleton(ContentModel.ASPECT_ROOT));
@@ -859,6 +861,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return node.getAclId();
}
+ @Override
public ChildAssocEntity newNode(
Long parentNodeId,
QName assocTypeQName,
@@ -866,6 +869,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
StoreRef storeRef,
String uuid,
QName nodeTypeQName,
+ Locale nodeLocale,
String childNodeName,
Map auditableProperties) throws InvalidTypeException
{
@@ -905,7 +909,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Get the store
StoreEntity store = getStoreNotNull(storeRef);
// Create the node (it is not a root node)
- NodeEntity node = newNodeImpl(store, uuid, nodeTypeQName, childAclId, false, auditableProps);
+ NodeEntity node = newNodeImpl(store, uuid, nodeTypeQName, nodeLocale, childAclId, false, auditableProps);
Long nodeId = node.getId();
// Protect the node's cm:auditable if it was explicitly set
@@ -947,6 +951,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
/**
* @param uuid the node UUID, or null to auto-generate
+ * @param nodeTypeQName the node's type
+ * @param nodeLocale the node's locale or null to use the default locale
* @param aclId an ACL ID if available
* @param auditableProps null to auto-generate or provide a value to explicitly set
* @param deleted true to create an already-deleted node (used for leaving trails of moved nodes)
@@ -955,6 +961,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
StoreEntity store,
String uuid,
QName nodeTypeQName,
+ Locale nodeLocale,
Long aclId,
boolean deleted,
AuditablePropertiesEntity auditableProps) throws InvalidTypeException
@@ -974,6 +981,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// QName
Long typeQNameId = qnameDAO.getOrCreateQName(nodeTypeQName).getFirst();
node.setTypeQNameId(typeQNameId);
+ // Locale
+ final Long localeId = localeDAO.getOrCreateLocalePair(nodeLocale).getFirst();
+ node.setLocaleId(localeId);
// ACL (may be null)
node.setAclId(aclId);
// Deleted
@@ -1163,7 +1173,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return assocPair;
}
- public void updateNode(Long nodeId, StoreRef storeRef, String uuid, QName nodeTypeQName)
+ @Override
+ public void updateNode(Long nodeId, StoreRef storeRef, String uuid, QName nodeTypeQName, Locale nodeLocale)
{
// Get the existing node; we need to check for a change in store or UUID
Node oldNode = getNodeNotNull(nodeId);
@@ -1176,10 +1187,23 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
uuid = oldNode.getUuid();
}
+ final Long nodeTypeQNameId;
if (nodeTypeQName == null)
{
- Long nodeTypeQNameId = oldNode.getTypeQNameId();
- nodeTypeQName = qnameDAO.getQName(nodeTypeQNameId).getSecond();
+ nodeTypeQNameId = oldNode.getTypeQNameId();
+ }
+ else
+ {
+ nodeTypeQNameId = qnameDAO.getOrCreateQName(nodeTypeQName).getFirst();
+ }
+ final Long nodeLocaleId;
+ if (nodeLocale == null)
+ {
+ nodeLocaleId = oldNode.getLocaleId();
+ }
+ else
+ {
+ nodeLocaleId = localeDAO.getOrCreateLocalePair(nodeLocale).getFirst();
}
// Wrap all the updates into one
@@ -1207,33 +1231,25 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
nodeUpdate.setUuid(oldNode.getUuid()); // Need node reference
}
// TypeQName (if necessary)
- Long nodeTypeQNameId = qnameDAO.getOrCreateQName(nodeTypeQName).getFirst();
if (!nodeTypeQNameId.equals(oldNode.getTypeQNameId()))
{
nodeUpdate.setTypeQNameId(nodeTypeQNameId);
nodeUpdate.setUpdateTypeQNameId(true);
}
+ // Locale (if necessary)
+ if (!nodeLocaleId.equals(oldNode.getLocaleId()))
+ {
+ nodeUpdate.setLocaleId(nodeLocaleId);
+ nodeUpdate.setUpdateLocaleId(true);
+ }
updateNodeImpl(oldNode, nodeUpdate);
}
/**
* Updates the node's transaction and cm:auditable properties only.
- *
- * @see #touchNodeImpl(Long, AuditablePropertiesEntity)
*/
private void touchNodeImpl(Long nodeId)
- {
- touchNodeImpl(nodeId, null);
- }
- /**
- * Updates the node's transaction and cm:auditable properties only.
- *
- * @param auditableProps optionally override the cm:auditable values
- *
- * @see #updateNodeImpl(NodeEntity, NodeUpdateEntity)
- */
- private void touchNodeImpl(Long nodeId, AuditablePropertiesEntity auditableProps)
{
Node node = null;
try
@@ -1248,10 +1264,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
}
NodeUpdateEntity nodeUpdate = new NodeUpdateEntity();
nodeUpdate.setId(nodeId);
- if (auditableProps != null)
- {
- nodeUpdate.setAuditableProperties(auditableProps);
- }
+ // Update it
updateNodeImpl(node, nodeUpdate);
}
@@ -1290,6 +1303,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
nodeUpdate.setTypeQNameId(oldNode.getTypeQNameId());
}
+ if (!nodeUpdate.isUpdateLocaleId())
+ {
+ nodeUpdate.setLocaleId(oldNode.getLocaleId());
+ }
if (!nodeUpdate.isUpdateAclId())
{
nodeUpdate.setAclId(oldNode.getAclId());
@@ -1392,7 +1409,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
Long liveNodeId = liveNode.getId();
String liveNodeUuid = GUID.generate();
- updateNode(liveNodeId, null, liveNodeUuid, null);
+ updateNode(liveNodeId, null, liveNodeUuid, null, null);
}
NodeEntity deletedNode = selectNodeByNodeRef(targetNodeRef, true); // Only look for deleted nodes
if (deletedNode != null)
@@ -1443,7 +1460,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
StoreEntity oldStore = oldNode.getStore();
String oldUuid = oldNode.getUuid();
- newNodeImpl(oldStore, oldUuid, ContentModel.TYPE_CMOBJECT, null, true, null);
+ newNodeImpl(oldStore, oldUuid, ContentModel.TYPE_CMOBJECT, null, null, true, null);
}
// Ensure that cm:auditable values are propagated, if required
@@ -1731,6 +1748,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Node node = getNodeNotNull(nodeId);
// Handle sys:referenceable
ReferenceablePropertiesEntity.addReferenceableProperties(node, props);
+ // Handle sys:localized
+ LocalizedPropertiesEntity.addLocalizedProperties(localeDAO, node, props);
// Handle cm:auditable
if (hasNodeAspect(nodeId, ContentModel.ASPECT_AUDITABLE))
{
@@ -1770,6 +1789,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Node node = getNodeNotNull(nodeId);
value = ReferenceablePropertiesEntity.getReferenceableProperty(node, propertyQName);
}
+ else if (LocalizedPropertiesEntity.isLocalizedProperty(propertyQName)) // sys:localized
+ {
+ Node node = getNodeNotNull(nodeId);
+ value = LocalizedPropertiesEntity.getLocalizedProperty(localeDAO, node, propertyQName);
+ }
else
{
Map props = getNodePropertiesCached(nodeId);
@@ -1811,16 +1835,20 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
return false; // No point adding nothing
}
-
+
+ // Get the current node
Node node = getNodeNotNull(nodeId);
+ // Create an update node
+ NodeUpdateEntity nodeUpdate = new NodeUpdateEntity();
+ nodeUpdate.setId(nodeId);
+
// Copy inbound values
newProps = new HashMap(newProps);
// Copy cm:auditable
- AuditablePropertiesEntity auditableProps = null;
if (!policyBehaviourFilter.isEnabled(node.getNodeRef(), ContentModel.ASPECT_AUDITABLE))
{
- auditableProps = node.getAuditableProperties();
+ AuditablePropertiesEntity auditableProps = node.getAuditableProperties();
if (auditableProps == null)
{
auditableProps = new AuditablePropertiesEntity();
@@ -1831,13 +1859,35 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// The behaviour is disabled, but no audit properties were passed in
auditableProps = null;
}
+ nodeUpdate.setAuditableProperties(auditableProps);
+ // We DON'T set the update flag because the update depends on the aspect being enabled, etc.
+ // nodeUpdate.setUpdateAuditableProperties(true);
}
// Remove cm:auditable
newProps.keySet().removeAll(AuditablePropertiesEntity.getAuditablePropertyQNames());
+
+ // Check if the sys:localized property is being changed
+ Long oldNodeLocaleId = node.getLocaleId();
+ Locale newLocale = DefaultTypeConverter.INSTANCE.convert(
+ Locale.class,
+ newProps.get(ContentModel.PROP_LOCALE));
+ if (newLocale != null)
+ {
+ Long newNodeLocaleId = localeDAO.getOrCreateLocalePair(newLocale).getFirst();
+ if (!newNodeLocaleId.equals(oldNodeLocaleId))
+ {
+ nodeUpdate.setLocaleId(newNodeLocaleId);
+ nodeUpdate.setUpdateLocaleId(true);
+ }
+ }
+ // else: a 'null' new locale is completely ignored. This is the behaviour we choose.
+
+ // Remove sys:localized
+ LocalizedPropertiesEntity.removeLocalizedProperties(node, newProps);
+
// Remove sys:referenceable
ReferenceablePropertiesEntity.removeReferenceableProperties(node, newProps);
-
// Load the current properties.
// This means that we have to go to the DB during cold-write operations,
// but usually a write occurs after a node has been fetched of viewed in
@@ -1910,10 +1960,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
}
}
- boolean updated = propsToDelete.size() > 0 || propsToAdd.size() > 0;
+ boolean modifyProps = propsToDelete.size() > 0 || propsToAdd.size() > 0;
+ boolean updated = modifyProps || nodeUpdate.isUpdateAnything();
// Touch to bring into current txn
- if (updated)
+ if (modifyProps)
{
// Clean up content properties
try
@@ -1976,9 +2027,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
setNodePropertiesCached(nodeId, propsToCache);
}
// Touch to bring into current transaction
- if (updated || auditableProps != null)
+ if (updated)
{
- touchNodeImpl(nodeId, auditableProps);
+ updateNodeImpl(node, nodeUpdate);
}
// Done
@@ -1986,8 +2037,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
logger.debug(
"Modified node properties: " + nodeId + "\n" +
- " Removed: " + propsToDelete + "\n" +
- " Added: " + propsToAdd);
+ " Removed: " + propsToDelete + "\n" +
+ " Added: " + propsToAdd + "\n" +
+ " Node Update: " + nodeUpdate);
}
return updated;
}
@@ -2030,6 +2082,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
return false; // sys:referenceable properties cannot be removed
}
+ LocalizedPropertiesEntity.removeLocalizedProperties(propertyQNames);
+ if (propertyQNames.size() == 0)
+ {
+ return false; // sys:localized properties cannot be removed
+ }
Set qnameIds = qnameDAO.convertQNamesToIds(propertyQNames, false);
int deleteCount = deleteNodeProperties(nodeId, qnameIds);
@@ -2126,6 +2183,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Set nodeAspects = getNodeAspectsCached(nodeId);
// Nodes are always referenceable
nodeAspects.add(ContentModel.ASPECT_REFERENCEABLE);
+ // Nodes are always localized
+ nodeAspects.add(ContentModel.ASPECT_LOCALIZED);
return nodeAspects;
}
@@ -2136,6 +2195,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Nodes are always referenceable
return true;
}
+ if (aspectQName.equals(ContentModel.ASPECT_LOCALIZED))
+ {
+ // Nodes are always localized
+ return true;
+ }
Set nodeAspects = getNodeAspectsCached(nodeId);
return nodeAspects.contains(aspectQName);
}
@@ -2153,6 +2217,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Find out what needs adding
aspectQNamesToAdd.removeAll(existingAspectQNames);
aspectQNamesToAdd.remove(ContentModel.ASPECT_REFERENCEABLE); // Implicit
+ aspectQNamesToAdd.remove(ContentModel.ASPECT_LOCALIZED); // Implicit
if (aspectQNamesToAdd.isEmpty())
{
// Nothing to do
@@ -2533,7 +2598,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// node into the current transaction for secondary associations
if (!isPrimary)
{
- updateNode(childNodeId, null, null, null);
+ updateNode(childNodeId, null, null, null, null);
}
// Done
diff --git a/source/java/org/alfresco/repo/domain/node/LocalizedPropertiesEntity.java b/source/java/org/alfresco/repo/domain/node/LocalizedPropertiesEntity.java
new file mode 100644
index 0000000000..486eff57e3
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/node/LocalizedPropertiesEntity.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005-2010 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.domain.node;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.domain.locale.LocaleDAO;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Class holding properties associated with the sys:localized aspect.
+ * This aspect is common enough to warrant direct inclusion on the Node entity.
+ *
+ * @author Derek Hulley
+ * @since 4.0
+ */
+public class LocalizedPropertiesEntity
+{
+ private static final Set LOCALIZED_PROP_QNAMES;
+ static
+ {
+ LOCALIZED_PROP_QNAMES = new HashSet(8);
+ LOCALIZED_PROP_QNAMES.add(ContentModel.PROP_LOCALE);
+ }
+
+ /**
+ * @return Returns true if the property belongs to the sys:localized aspect
+ */
+ public static boolean isLocalizedProperty(QName qname)
+ {
+ return LOCALIZED_PROP_QNAMES.contains(qname);
+ }
+
+ /**
+ * Remove all {@link ContentModel#ASPECT_LOCALIZED localized} properties
+ */
+ public static void removeLocalizedProperties(Node node, Map properties)
+ {
+ properties.keySet().removeAll(LOCALIZED_PROP_QNAMES);
+ }
+
+ /**
+ * Remove all {@link ContentModel#ASPECT_LOCALIZED localized} properties
+ */
+ public static void removeLocalizedProperties(Set propertyQNames)
+ {
+ propertyQNames.removeAll(LOCALIZED_PROP_QNAMES);
+ }
+
+ /**
+ * Adds all {@link ContentModel#ASPECT_LOCALIZED localized} properties.
+ */
+ public static void addLocalizedProperties(LocaleDAO localeDAO, Node node, Map properties)
+ {
+ Long localeId = node.getLocaleId();
+ Locale locale = localeDAO.getLocalePair(localeId).getSecond();
+ properties.put(ContentModel.PROP_LOCALE, locale);
+ }
+
+ public static Serializable getLocalizedProperty(LocaleDAO localeDAO, Node node, QName qname)
+ {
+ if (qname.equals(ContentModel.PROP_LOCALE))
+ {
+ Long localeId = node.getLocaleId();
+ return localeDAO.getLocalePair(localeId).getSecond();
+ }
+ throw new IllegalArgumentException("Not sys:localized property: " + qname);
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/node/Node.java b/source/java/org/alfresco/repo/domain/node/Node.java
index af05ca621d..2440f3006c 100644
--- a/source/java/org/alfresco/repo/domain/node/Node.java
+++ b/source/java/org/alfresco/repo/domain/node/Node.java
@@ -30,6 +30,8 @@ public interface Node extends NodeIdAndAclId
{
public abstract NodeRef getNodeRef();
+ public NodeRef.Status getNodeStatus();
+
public abstract Pair getNodePair();
public abstract Long getVersion();
@@ -39,6 +41,8 @@ public interface Node extends NodeIdAndAclId
public abstract String getUuid();
public abstract Long getTypeQNameId();
+
+ public abstract Long getLocaleId();
public abstract Boolean getDeleted();
diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
index 00c860ccd1..537302468c 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
@@ -21,6 +21,7 @@ package org.alfresco.repo.domain.node;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -148,6 +149,7 @@ public interface NodeDAO extends NodeBulkLoader
* @param storeRef the store to which the node must belong
* @param uuid the node store-unique identifier, or null to assign a GUID
* @param nodeTypeQName the type of the node
+ * @parma nodeLocale the locale of the node
* @param childNodeName the cm:name of the child node or null to use the node's UUID
* @param auditableProperties a map containing any cm:auditable properties for the node
* @return Returns the details of the child association created
@@ -161,6 +163,7 @@ public interface NodeDAO extends NodeBulkLoader
StoreRef storeRef,
String uuid,
QName nodeTypeQName,
+ Locale nodeLocale,
String childNodeName,
Map auditableProperties/*,
Map ownableProperties*/) throws InvalidTypeException;
@@ -188,8 +191,9 @@ public interface NodeDAO extends NodeBulkLoader
* @param storeRef the new store or null to keep the existing one
* @param uuid the new UUID for the node or null to keep it the same
* @param nodeTypeQName the new type QName for the node or null to keep the existing one
+ * @param nodeLocale the new locale for the node or null to keep the existing one
*/
- public void updateNode(Long nodeId, StoreRef storeRef, String uuid, QName nodeTypeQName);
+ public void updateNode(Long nodeId, StoreRef storeRef, String uuid, QName nodeTypeQName, Locale nodeLocale);
public void setNodeAclId(Long nodeId, Long aclId);
diff --git a/source/java/org/alfresco/repo/domain/node/NodeEntity.java b/source/java/org/alfresco/repo/domain/node/NodeEntity.java
index adccc4a602..ed37001c87 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeEntity.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeEntity.java
@@ -36,6 +36,7 @@ public class NodeEntity implements Node
private StoreEntity store;
private String uuid;
private Long typeQNameId;
+ private Long localeId;
private Long aclId;
private Boolean deleted;
private TransactionEntity transaction;
@@ -71,6 +72,7 @@ public class NodeEntity implements Node
this.store = node.getStore();
this.uuid = node.getUuid();
this.typeQNameId = node.getTypeQNameId();
+ this.localeId = node.getLocaleId();
this.aclId = node.getAclId();
this.deleted = node.getDeleted();
this.transaction = node.getTransaction();
@@ -93,6 +95,7 @@ public class NodeEntity implements Node
}
sb.append(", uuid=").append(uuid)
.append(", typeQNameId=").append(typeQNameId)
+ .append(", localeId=").append(localeId)
.append(", aclId=").append(aclId)
.append(", deleted=").append(deleted)
.append(", transaction=").append(transaction)
@@ -129,22 +132,26 @@ public class NodeEntity implements Node
}
}
+ @Override
public NodeRef getNodeRef()
{
return new NodeRef(store.getStoreRef(), uuid);
}
+ @Override
public NodeRef.Status getNodeStatus()
{
NodeRef nodeRef = new NodeRef(store.getStoreRef(), uuid);
return new NodeRef.Status(nodeRef, transaction.getChangeTxnId(), transaction.getId(), deleted);
}
+ @Override
public Pair getNodePair()
{
return new Pair(id, getNodeRef());
}
+ @Override
public Long getId()
{
return id;
@@ -156,6 +163,7 @@ public class NodeEntity implements Node
this.id = id;
}
+ @Override
public Long getVersion()
{
return version;
@@ -167,6 +175,7 @@ public class NodeEntity implements Node
this.version = version;
}
+ @Override
public StoreEntity getStore()
{
return store;
@@ -178,6 +187,7 @@ public class NodeEntity implements Node
this.store = store;
}
+ @Override
public String getUuid()
{
return uuid;
@@ -189,6 +199,7 @@ public class NodeEntity implements Node
this.uuid = uuid;
}
+ @Override
public Long getTypeQNameId()
{
return typeQNameId;
@@ -200,6 +211,18 @@ public class NodeEntity implements Node
this.typeQNameId = typeQNameId;
}
+ @Override
+ public Long getLocaleId()
+ {
+ return localeId;
+ }
+
+ public void setLocaleId(Long localeId)
+ {
+ this.localeId = localeId;
+ }
+
+ @Override
public Long getAclId()
{
return aclId;
@@ -211,6 +234,7 @@ public class NodeEntity implements Node
this.aclId = aclId;
}
+ @Override
public Boolean getDeleted()
{
return deleted;
@@ -222,6 +246,7 @@ public class NodeEntity implements Node
this.deleted = deleted;
}
+ @Override
public TransactionEntity getTransaction()
{
return transaction;
@@ -233,6 +258,7 @@ public class NodeEntity implements Node
this.transaction = transaction;
}
+ @Override
public AuditablePropertiesEntity getAuditableProperties()
{
return auditableProperties;
diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java b/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java
index 43ae0c090a..cb54ec363f 100644
--- a/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java
+++ b/source/java/org/alfresco/repo/domain/node/NodePropertyHelper.java
@@ -44,7 +44,6 @@ import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.extensions.surf.util.I18NUtil;
/**
* This class provides services for translating exploded properties
diff --git a/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java b/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java
index da8cca8531..435e02671f 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java
@@ -30,6 +30,7 @@ public class NodeUpdateEntity extends NodeEntity
private boolean updateStore;
private boolean updateUuid;
private boolean updateTypeQNameId;
+ private boolean updateLocaleId;
private boolean updateAclId;
private boolean updateDeleted;
private boolean updateTransaction;
@@ -48,7 +49,7 @@ public class NodeUpdateEntity extends NodeEntity
public boolean isUpdateAnything()
{
return updateAuditableProperties || updateTransaction || updateDeleted
- || updateAclId || updateStore || updateUuid || updateTypeQNameId;
+ || updateLocaleId || updateAclId || updateStore || updateUuid || updateTypeQNameId;
}
public boolean isUpdateStore()
@@ -81,6 +82,16 @@ public class NodeUpdateEntity extends NodeEntity
this.updateTypeQNameId = updateTypeQNameId;
}
+ public boolean isUpdateLocaleId()
+ {
+ return updateLocaleId;
+ }
+
+ public void setUpdateLocaleId(boolean updateLocaleId)
+ {
+ this.updateLocaleId = updateLocaleId;
+ }
+
public boolean isUpdateAclId()
{
return updateAclId;
diff --git a/source/java/org/alfresco/repo/domain/node/ReferenceablePropertiesEntity.java b/source/java/org/alfresco/repo/domain/node/ReferenceablePropertiesEntity.java
index d956b754cd..fac8eec182 100644
--- a/source/java/org/alfresco/repo/domain/node/ReferenceablePropertiesEntity.java
+++ b/source/java/org/alfresco/repo/domain/node/ReferenceablePropertiesEntity.java
@@ -98,7 +98,6 @@ public class ReferenceablePropertiesEntity
public static Serializable getReferenceableProperty(Node node, QName qname)
{
- Long nodeId = node.getId();
NodeRef nodeRef = node.getNodeRef();
if (qname.equals(ContentModel.PROP_STORE_PROTOCOL))
{
@@ -114,7 +113,7 @@ public class ReferenceablePropertiesEntity
}
else if (qname.equals(ContentModel.PROP_NODE_DBID))
{
- return nodeId;
+ return node.getId();
}
throw new IllegalArgumentException("Not sys:referenceable property: " + qname);
}
diff --git a/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java b/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java
index 2de626c4dc..46b753b2cf 100644
--- a/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java
@@ -480,6 +480,7 @@ public class AclDAOImpl implements AclDAO
{
acl.setInheritsFrom(inheritsFrom);
}
+ acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
@@ -977,6 +978,7 @@ public class AclDAOImpl implements AclDAO
if (acl.isVersioned())
{
acl.setLatest(Boolean.FALSE);
+ acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
}
else
@@ -1162,6 +1164,8 @@ public class AclDAOImpl implements AclDAO
inheritedAclId = acl.getId();
}
+ // Does not cause the change set to change
+ //acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
return inheritedAclId;
}
@@ -1328,6 +1332,7 @@ public class AclDAOImpl implements AclDAO
throw new IllegalArgumentException("Fixed and global permissions can not inherit");
case OLD:
acl.setInherits(Boolean.TRUE);
+ acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
@@ -1346,6 +1351,7 @@ public class AclDAOImpl implements AclDAO
getWritable(id, null, null, null, null, false, changes, WriteMode.COPY_ONLY);
acl = aclCrudDAO.getAclForUpdate(changes.get(0).getAfter());
acl.setInherits(Boolean.TRUE);
+ acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
}
else
@@ -1376,6 +1382,7 @@ public class AclDAOImpl implements AclDAO
return Collections. singletonList(new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType()));
case OLD:
acl.setInherits(Boolean.FALSE);
+ acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
@@ -1410,6 +1417,7 @@ public class AclDAOImpl implements AclDAO
case COW:
aclToCopy = aclCrudDAO.getAclForUpdate(toCopy);
aclToCopy.setRequiresVersion(true);
+ aclToCopy.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(aclToCopy);
aclCache.remove(toCopy);
readersCache.remove(toCopy);
@@ -1418,6 +1426,7 @@ public class AclDAOImpl implements AclDAO
{
AclUpdateEntity inheritedAcl = aclCrudDAO.getAclForUpdate(inheritedId);
inheritedAcl.setRequiresVersion(true);
+ inheritedAcl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(inheritedAcl);
aclCache.remove(inheritedId);
readersCache.remove(inheritedId);
@@ -1594,6 +1603,7 @@ public class AclDAOImpl implements AclDAO
AclUpdateEntity acl = aclCrudDAO.getAclForUpdate(changes.get(0).getAfter());
final Long inheritsFrom = acl.getInheritsFrom();
acl.setInherits(Boolean.FALSE);
+ acl.setAclChangeSetId(getCurrentChangeSetId());
aclCrudDAO.updateAcl(acl);
// Keep inherits from so we can reinstate if required
diff --git a/source/java/org/alfresco/repo/domain/usage/UsageDAOTest.java b/source/java/org/alfresco/repo/domain/usage/UsageDAOTest.java
index c175180c74..9609878269 100644
--- a/source/java/org/alfresco/repo/domain/usage/UsageDAOTest.java
+++ b/source/java/org/alfresco/repo/domain/usage/UsageDAOTest.java
@@ -32,6 +32,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
+import org.springframework.extensions.surf.util.I18NUtil;
/**
* @see UsageDAO
@@ -80,6 +81,7 @@ public class UsageDAOTest extends TestCase
storeRef,
null,
ContentModel.TYPE_CONTENT,
+ I18NUtil.getLocale(),
null,
null);
diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
index 1fb8e66c91..d2bd3615f4 100644
--- a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
+++ b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
@@ -252,7 +252,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
- assertEquals("Expecting to find 18 fields", 18, fieldDefs.size());
+ assertEquals("Wrong number of fields", 19, fieldDefs.size());
// create a Map of the field definitions
// NOTE: we can safely do this as we know there are no duplicate field names and we're not
@@ -650,7 +650,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
- assertEquals("Expecting to find 7 fields", 7, fieldDefs.size());
+ assertEquals("Wrong number of fields", 8, fieldDefs.size());
// create a Map of the field definitions
// NOTE: we can safely do this as we know there are no duplicate field names and we're not
@@ -829,7 +829,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
- assertEquals("Expecting to find 7 fields", 7, fieldDefs.size());
+ assertEquals("Wrong number of fields", 8, fieldDefs.size());
// create a Map of the field definitions
// NOTE: we can safely do this as we know there are no duplicate field names and we're not
diff --git a/source/java/org/alfresco/repo/forms/script/test_formService.js b/source/java/org/alfresco/repo/forms/script/test_formService.js
index 3b999e48e5..ffe18df130 100644
--- a/source/java/org/alfresco/repo/forms/script/test_formService.js
+++ b/source/java/org/alfresco/repo/forms/script/test_formService.js
@@ -32,7 +32,7 @@ function testGetFormForContentNode()
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
- test.assertEquals(18, fieldDefs.length);
+ test.assertEquals(19, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test
@@ -154,7 +154,7 @@ function testGetFormForFolderNode()
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
- test.assertEquals(7, fieldDefs.length);
+ test.assertEquals(8, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test
diff --git a/source/java/org/alfresco/repo/forum/CommentsRollupAspect.java b/source/java/org/alfresco/repo/forum/CommentsRollupAspect.java
new file mode 100644
index 0000000000..9a7c7f84c0
--- /dev/null
+++ b/source/java/org/alfresco/repo/forum/CommentsRollupAspect.java
@@ -0,0 +1,89 @@
+/*
+ * 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.forum;
+
+import org.alfresco.model.ForumModel;
+import org.alfresco.model.RenditionModel;
+import org.alfresco.repo.copy.CopyBehaviourCallback;
+import org.alfresco.repo.copy.CopyDetails;
+import org.alfresco.repo.copy.CopyServicePolicies;
+import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
+import org.alfresco.repo.policy.JavaBehaviour;
+import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * {@link ForumModel#ASPECT_COMMENTS_ROLLUP comments rollup} aspect behaviour bean.
+ * This aspect should not be copied.
+ *
+ * @author Neil Mc Erlean
+ * @since 4.0
+ */
+public class CommentsRollupAspect implements CopyServicePolicies.OnCopyNodePolicy
+{
+ private PolicyComponent policyComponent;
+
+ /**
+ * Set the policy component
+ *
+ * @param policyComponent policy component
+ */
+ public void setPolicyComponent(PolicyComponent policyComponent)
+ {
+ this.policyComponent = policyComponent;
+ }
+
+ /**
+ * Initialise method
+ */
+ public void init()
+ {
+ this.policyComponent.bindClassBehaviour(
+ QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
+ RenditionModel.ASPECT_RENDITIONED,
+ new JavaBehaviour(this, "getCopyCallback"));
+ }
+
+ /**
+ * @return Returns {@link CommentsRollupAspectCopyBehaviourCallback}
+ */
+ public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
+ {
+ return CommentsRollupAspectCopyBehaviourCallback.INSTANCE;
+ }
+
+ /**
+ * Behaviour for the {@link ForumModel#ASPECT_COMMENTS_ROLLUP fm:commentsRollup} aspect.
+ */
+ private static class CommentsRollupAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
+ {
+ private static final CopyBehaviourCallback INSTANCE = new CommentsRollupAspectCopyBehaviourCallback();
+
+ /**
+ * We do not copy the {@link ForumModel#ASPECT_COMMENTS_ROLLUP fm:commentsRollup} aspect.
+ */
+ @Override
+ public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
+ {
+ // Prevent the copying of the aspect.
+ return false;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/forum/ForumPostBehaviours.java b/source/java/org/alfresco/repo/forum/ForumPostBehaviours.java
index 78ef889604..3e3503508f 100644
--- a/source/java/org/alfresco/repo/forum/ForumPostBehaviours.java
+++ b/source/java/org/alfresco/repo/forum/ForumPostBehaviours.java
@@ -20,13 +20,14 @@ package org.alfresco.repo.forum;
import org.alfresco.model.ForumModel;
import org.alfresco.repo.node.NodeServicePolicies;
+import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy;
-import org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
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;
/**
* This class registers behaviours for the {@link ForumModel#TYPE_POST fm:post} content type.
@@ -36,7 +37,7 @@ import org.alfresco.service.cmr.repository.NodeService;
* @since 4.0
*/
public class ForumPostBehaviours implements NodeServicePolicies.OnCreateNodePolicy,
- NodeServicePolicies.OnDeleteNodePolicy
+ NodeServicePolicies.BeforeDeleteNodePolicy
{
private PolicyComponent policyComponent;
private NodeService nodeService;
@@ -61,9 +62,9 @@ public class ForumPostBehaviours implements NodeServicePolicies.OnCreateNodePoli
ForumModel.TYPE_POST,
new JavaBehaviour(this, "onCreateNode"));
this.policyComponent.bindClassBehaviour(
- OnDeleteNodePolicy.QNAME,
+ BeforeDeleteNodePolicy.QNAME,
ForumModel.TYPE_POST,
- new JavaBehaviour(this, "onDeleteNode"));
+ new JavaBehaviour(this, "beforeDeleteNode"));
}
@Override
@@ -81,13 +82,12 @@ public class ForumPostBehaviours implements NodeServicePolicies.OnCreateNodePoli
}
@Override
- public void onDeleteNode(ChildAssociationRef childAssocRef,
- boolean isNodeArchived)
+ public void beforeDeleteNode(NodeRef nodeRef)
{
// We have one less comment under a discussable node.
// We need to find the fm:commentsRollup ancestor to this comment node and decrement its commentCount
-
- NodeRef commentsRollupNode = getCommentsRollupAncestor(childAssocRef.getParentRef());
+ NodeRef topicNode = nodeService.getPrimaryParent(nodeRef).getParentRef();
+ NodeRef commentsRollupNode = getCommentsRollupAncestor(topicNode);
if (commentsRollupNode != null)
{
@@ -102,19 +102,37 @@ public class ForumPostBehaviours implements NodeServicePolicies.OnCreateNodePoli
* {@link ForumModel#ASPECT_COMMENTS_ROLLUP commentsRollup} aspect.
*
* @param topicNode
- * @return the NodeRef of the commentsRollup ancestor if there is one (which there always should be), else null
.
+ * @return the NodeRef of the commentsRollup ancestor if there is one, else null
.
*/
private NodeRef getCommentsRollupAncestor(NodeRef topicNode)
{
+ // We are specifically trying to roll up "comment" counts here. In other words the number of "comments" on a node
+ // as applied through the Share UI.
+ // We are not trying to roll up generic fm:post counts. Although, of course, comments are modelled as fm:post nodes.
+ // So there are two scenarios in which we do not want to roll up changes to the count.
+ // 1. When the fm:post node is not a Share comment.
+ // 2. When the node is being deleted as part of a cascade delete.
+ // If an ancestor node to an fm:post is deleted then the parent structure may have been flattened within the archive store.
+ //
+ NodeRef result = null;
+
NodeRef forumNode = nodeService.getPrimaryParent(topicNode).getParentRef();
- NodeRef commentsRollupNode = nodeService.getPrimaryParent(forumNode).getParentRef();
- if (! nodeService.hasAspect(commentsRollupNode, ForumModel.ASPECT_COMMENTS_ROLLUP))
+ if (ForumModel.TYPE_FORUM.equals(nodeService.getType(forumNode)) && !forumNode.getStoreRef().equals(StoreRef.PROTOCOL_ARCHIVE))
{
- return null;
- }
- else
- {
- return commentsRollupNode;
+ NodeRef commentsRollupNode = nodeService.getPrimaryParent(forumNode).getParentRef();
+
+ if (!commentsRollupNode.getStoreRef().equals(StoreRef.PROTOCOL_ARCHIVE))
+ {
+ if (! nodeService.hasAspect(commentsRollupNode, ForumModel.ASPECT_COMMENTS_ROLLUP))
+ {
+ result = null;
+ }
+ else
+ {
+ result = commentsRollupNode;
+ }
+ }
}
+ return result;
}
}
diff --git a/source/java/org/alfresco/repo/jscript/ValueConverter.java b/source/java/org/alfresco/repo/jscript/ValueConverter.java
index 2c3b1fc396..b0dcb3e374 100644
--- a/source/java/org/alfresco/repo/jscript/ValueConverter.java
+++ b/source/java/org/alfresco/repo/jscript/ValueConverter.java
@@ -117,7 +117,8 @@ public class ValueConverter
try
{
Context.enter();
- // convert array to a native JavaScript Array
+ // Convert array to a native JavaScript Array
+ // Note - a scope is usually required for this to work
value = (Serializable)Context.getCurrentContext().newArray(scope, array);
}
finally
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
index bf4c8cdc05..fe85aa6414 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.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
*
@@ -41,7 +41,6 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
-import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -86,6 +85,9 @@ public class FileFolderPerformanceTester extends TestCase
private NodeRef rootFolderRef;
private File dataFile;
+ private String USERNAME = AuthenticationUtil.getAdminUserName(); // as admin
+ //private String USERNAME = AuthenticationUtil.getSystemUserName(); // as system (bypass permissions)
+
protected NodeService getNodeService()
{
@@ -103,14 +105,26 @@ public class FileFolderPerformanceTester extends TestCase
searchService = serviceRegistry.getSearchService();
nodeService = getNodeService();
- // authenticate
- authenticationComponent.setSystemUserAsCurrentUser();
+ authenticate(USERNAME);
rootFolderRef = getOrCreateRootFolder();
dataFile = AbstractContentTransformerTest.loadQuickTestFile("txt");
}
+
+ private void authenticate(String userName)
+ {
+ if (AuthenticationUtil.getSystemUserName().equals(userName))
+ {
+ authenticationComponent.setSystemUserAsCurrentUser();
+ }
+ else
+ {
+ authenticationComponent.setCurrentUser(userName);
+ }
+ }
+
public void testSetUp() throws Exception
{
assertNotNull(dataFile);
@@ -118,7 +132,7 @@ public class FileFolderPerformanceTester extends TestCase
protected NodeRef getOrCreateRootFolder()
{
- // find the guest folder
+ // find the company home folder
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
ResultSet rs = searchService.query(storeRef, SearchService.LANGUAGE_XPATH, "/app:company_home");
try
@@ -185,7 +199,7 @@ public class FileFolderPerformanceTester extends TestCase
public void run()
{
// authenticate
- authenticationComponent.setSystemUserAsCurrentUser();
+ authenticate(USERNAME);
// progress around the folders until they have been populated
start = System.currentTimeMillis();
@@ -242,16 +256,20 @@ public class FileFolderPerformanceTester extends TestCase
long time = (end - start);
double average = (double) time / (double) (folderCount * currentBatchCount * filesPerBatch);
double percentComplete = (double) currentBatchCount / (double) batchCount * 100.0;
- logger.debug("\n" +
- "[" + Thread.currentThread().getName() + "] \n" +
- " Created " + (currentBatchCount*filesPerBatch) + " files in each of " + folderCount +
- " folders (" + (randomOrder ? "shuffled" : "in order") + "): \n" +
- " Progress: " + String.format("%9.2f", percentComplete) + " percent complete \n" +
- " Average: " + String.format("%10.2f", average) + " ms per file \n" +
- " Average: " + String.format("%10.2f", 1000.0/average) + " files per second");
+
+ if (percentComplete > 0)
+ {
+ logger.debug("\n" +
+ "[" + Thread.currentThread().getName() + "] \n" +
+ " Created " + (currentBatchCount*filesPerBatch) + " files in each of " + folderCount +
+ " folders (" + (randomOrder ? "shuffled" : "in order") + "): \n" +
+ " Progress: " + String.format("%9.2f", percentComplete) + " percent complete \n" +
+ " Average: " + String.format("%10.2f", average) + " ms per file \n" +
+ " Average: " + String.format("%10.2f", 1000.0/average) + " files per second");
+ }
}
};
-
+
// kick off the required number of threads
logger.debug("\n" +
"Starting " + threadCount +
@@ -282,35 +300,44 @@ public class FileFolderPerformanceTester extends TestCase
}
}
- @SuppressWarnings("unused")
private void readStructure(
final NodeRef parentNodeRef,
final int threadCount,
final int repetitions,
final double[] dumpPoints)
{
- final List children = nodeService.getChildAssocs(parentNodeRef);
+ final List children = fileFolderService.list(parentNodeRef);
Runnable runnable = new Runnable()
{
public void run()
{
// authenticate
- authenticationComponent.setSystemUserAsCurrentUser();
+ authenticate(USERNAME);
for (int i = 0; i < repetitions; i++)
{
// read the contents of each folder
- for (ChildAssociationRef childAssociationRef : children)
+ for (final FileInfo fileInfo : children)
{
- final NodeRef folderRef = childAssociationRef.getChildRef();
+ final NodeRef folderRef = fileInfo.getNodeRef();
RetryingTransactionCallback