From a1b12e2f0f9f9f90b4c8a7749b291e4626ba1104 Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Tue, 16 Oct 2012 14:20:20 +0000 Subject: [PATCH] Merged V4.1-BUG-FIX to HEAD 42174: ALF-14721: Merged PATCHES/V4.0.2 to V4.1-BUG-FIX 41782: ALF-15751: Merged DEV to V4.0.2 (4.0.2.14) 41704: ALF-15751: CLONE - Version History presents versions in wrong order 'VersionHistoryImpl' now sorts versions by node DB id because version with greater version number can't have id which is lesser than id of version with lesser version number. Additionally, this approach should be quicker than sorting by 'Modification date' and 'Version number' label. << Did not merge unit test, which was doing things with version branches that we don't normally support >> 42179: ALF-16149: Merged PATCHES/V4.0.1 to V4.1-BUG-FIX 41995: 41911: ALF-14127 User search retrieves all users from the DB regardless of search criteria - PeopleServiceImpl.getPeople(...) now calls a new method nonCannedGetPeopleQuery(...) rather than using the canned query which is slow with large numbers of users. 42011: 41911: ALF-14127 User search retrieves all users from the DB regardless of search criteria - Avoid NPE on params 42059: 41911: ALF-14127 User search retrieves all users from the DB regardless of search criteria - Ignore case broke one of the unit tests (now excluded from nonCanned version) 42188: French installer corrections from Gloria 42192: ALF-15906 - Share UI does not show the 'edit online' button for Visio documents 42195: Refactor of imapSpacesTemplates.acp into imapSpacesTemplates.xml and exploded content. This work is a necessary precursor to the fix for ALF-15803, which will add new localisations. 42220: Fix for ALF-16138. AbstractLinksWebScript doesn't cope with Links from deleted users. 42233: Fix for ALF-16164 Cloud monitoring of SOLR is CPU intensive due to its repeated use of the SOLR stats page and related CLOUD-760 Cloud monitoring of SOLR is CPU intensive due to its repeated use of the SOLR stats page 42259: Fix to issue where multiple concurrent writes to same user preferences would cause exception to appear in Share when changing between old document library views and new views provided by a module. 42266: ALF-16154 - IE9: script error when click on workflow from document details page 42268: Fix for ALF-11152 - License Usage information always shows 0 users 42269: Fix for ALF-15211 - TinyMCE corrupting hyperlinks 42275: ALF-15993: alfresco log not removed if uninstalled on a different day - Fix from Bitrock - Also fixed for awe and share logs 42289: Merged DEV to V4.1-BUG-FIX 42276: ALF-1907: Check out rule is active for spaces - Unit test for checkout via action executer Fixed line endings and split asserts 42292: ALF-15937: updated the Javadoc of the checkin method to be in sync with what's in doc.alfresco.com 42307: Fix handling of syncmodeconfig=OFF when running 4.1.X locally without doing full enterprise build. 42308: Fix ALF-13968: Share DocLib sorting mixes files and folders - implicitly sort folders before files (~ pre 4.x) then selected sort option, such as name - also allow Alf-specific option with CMIS getChildren (eg. "orderBy=cmis:baseTypeId DESC,cmis:name ASC") 42310: Merged BRANCHES/DEV/BELARUS/V4.1-BUG-FIX-2012_09_24 to BRANCHES/DEV/V4.1-BUG-FIX: 42309: ALF-15707 (ALF-14691) - Any custom aspect or type (including ootb workflow) is not available for API calls like api/classes/ 42338: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.1-BUG-FIX 42337: Fix for ALF-14764 - Moving a folder removes non-site Group permissions set, resets 'Inherit permissions flag' 42339: Fix for ALF-15151 - Selected group is illegible(black) in Admin console if High contrast theme is selected 42342: ALF-10362: Activities fail to log "name" changes with more than 1024 chars (eg. via Share "Create Content" form) - part I - fix Share config so that default "Create Content" form restricts to 255 chars as per other form config (eg. Edit Properties, inline rename, ...) 42353: Merged V3.4-BUG-FIX to V4.1-BUG-FIX (RECORD ONLY) 42281: Fix for ALF-9946 Need a supported solution for switching off content indexing (FTS) -> merge only to 4.1-BUG-FIX - remove references to isIndexed property which was removed in the back port 42360: ALF-16146: Fixed QName of the data list item type. 42361: ALF-10362: Activities fail to log "name" changes with more than 1024 chars (eg. via Share "Create Content" form) - part II - belts-and-braces (with unit test) 42362: Merged DEV to V4.1-BUG-FIX 42336: ALF-16160: office 2010 doesn't notify users of files being locked when using sharepoint protocol MS Office (if we enabled notification about document unlocking) periodically sends PROPFIND requests to get info about active locks. This code makes PROPFIND be able to send an info about locks for the MS Office 2010 client if a document was locked for edit offline. 42363: ALF-16213: renaming versioned file results in file being deleted. 42368: Record only merge V3.4-BUG-FIX (3.4.12) to V4.1-BUG-FIX (4.1.2) 42367: Merge V3.4 (3.4.11) to V3.4-BUG-FIX (3.4.12) 42366: ALF-16137: Merge V4.1 (4.1.1) to V3.4 (3.4.11) 42048: ALF-16005 Could not transform file size of 0 kb - Turns out that it was only doc ppt and xls zero byte files that had the problem. - Reverting part of revision 6473 (release 2.1 2007) AR-1251 (Version error when saving new content via CIFS) Dave W tells me that this is no longer an issue due to other changes 42381: Fixed ALF-16218: Solr GetNodes return status is 500 for Postgresql - Read-only webscript was calling through to "qnameDAO.getOrCreateQName", which could fail if the QName does not exist. Issue is not critical because it will start working once the QName gets created. 42384: ALF-15868 RepoTransferReceiverImplTest failing on MySQL Checked in a refactor of the transaction handling in the test to remove the suspicion that the current failure iis somehow a test error. (Still fails on MySQL) 42395: ALF-14353: Deploy pom files with dependencies to the Maven repo 42405: ALF-15986: Upgrade to Bitrock 8.5.0 in order to improve font scaling and adaptive layout with Gtk - Helps I18N 42407: Fixed 'state leak' from ActivityServiceImplTest 42408: Merged BRANCHES/DEV/FEATURES/CLOUD1_CLOUDSYNC to BRANCHES/DEV/V4.1-BUG-FIX: 42389: CLOUD-796: handle unknown custom content/folder type 42396: CLOUD-796: handle unknown custom content/folder type 42397: Merged BRANCHES/DEV/V4.1-BUG-FIX to BRANCHES/DEV/FEATURES/CLOUD1_CLOUDSYNC: 41858: ALF-14444 - CloudSync: Ensure unknown properties when synced to Cloud are ignored properly 42406: CLOUD-796: handle unknown custom content/folder type 42409: CloudSync: CLOUD-796 / ALF-16226 - hide sync props in forms (eg. edit props) for sync'ed custom content type 42419: Fixes: ALF-11096 - Ensures event edit button is disabled if the event came from Outlook (this is because VTI connector is one directional & changes can't be pushed back). 42420: Fix for ALF-16003 - Sync mode is incorrectly cached as off if repo hasn't started when the check is made. 42430: More refactoring of RepoTransferReceiverImplTest. 42441: Merged V4.1-BUG-FIX to HEAD 42440: ALF-16247: Thumbnails not rendering for PDFs with standard fonts - Because GS_LIB wasn't set on Linux and OSX 42452: Fix for ALF-15450 Share Lucene tool in admin console works incorrectly 42457: ALF-14347: Document workspace is incorrectly deleted - Check returned status code from delete method before continuing to delete components. 42458: ALF-15700: 'Imap Attachments' folder is not localized. - Added spaces.imap_attachments.childname property that allows the attachments folder to be localized 42459: ALF-16103: No easy way to specify a timeout for LDAP connections - Added ldap-authentication/ldap-ad-authentication property (ldap.authentication.java.naming.read.timeout) to configure the com.sun.jndi.ldap.read.timeout for the initialDirContextEnvironment. - ldap.authentication.java.naming.read.timeout property is configured in milliseconds. Defaults to zero (infinite) which is the current behavior. 42467: Fix for ALF-16275 SOLR include configuration to avoid indexing content - done and fixed all configuration to be treated as Java properties 42472: ALF-16175: Merged PATCHES/V4.0.1 to V4.1-BUG-FIX (Record Only) 42448: ALF-16096: Repo corruption in MT - clean-up assistance requested - Changed RepositoryAuthenticationDAO.getUserFolderLocation() to use getCurrentUserDomain() for its cache key. 42473: ALF-14838 ALF-14839 Deploy Maven artifacts containing the config and the test-resources, using these as classifiers 42475: ALF-14180 - CIFS - Cluster - doc and docx files are opened in read-only mode via MS Office 2003 and 2010 appropriately missed from check in 34544 42477: ALF-5051: Define ThumbnailDefinition Beans Outside of ThumbnailRegistry Bean - Reverted imgpreview to enterprise 4.1 size of 480 42504: Reverse Merge 42458 ALF-15700: 'Imap Attachments' folder is not localized. Causes unit test failures. 42517: ALF-15700: Restoring duff revision 42458 so that we can finish the job and fix it 42518: ALF-15700: Corrected internationalization of IMAP Attachments folder - RepositoryFolderConfigBean must look up paths by QName to be immune to localization and backward compatible - Must throw an error rather than using the store root if the path contains unresolved placeholders! - QName of attachments folder must remain "cm:Imap Attachments" because that's what it always was! 42528: ALF-16282: Hybrid Sync: folder unsync - sub-children still have sync indicators - fix typo fallout from ALF-15420 (r40782) + add unit/regress test 42529: ALF-16231: Corrected Imap Attachments English string 42530: ALF-14838 ALF-14839 Fix enterprise artifacts + deploy jars instead of zips 42531: ALF-14770 Cut / Paste triggers folder rules - Needed to disable rules on nodes being MOVED. - Added extra check to RuleTypeImpl when working out if a rule was disabled so that debug would not be misleading. No impact on logic, as RuleService does the same check later and discards the rules. 42546: ALF-15737 Audit trail does not show user login events - Also does not show any failed login events 42568: ALF-16077 CLONE: Incorrect activities if you try to add/edit/remove comment for document (if this document contains any title) The original activity feed comment code would include the title of a document, folder or blog rather than its name if it was available. - name is a mandatory field for a document and folder. - title is a mandatory field for a blog entry and its name may not be set via Share. Changing activity feed comment code so that the: - name is always used for documents and folders - title is always used for blogs 42571: ALF-14838 ALF-14839 Deploy config and test-resoruces artifacts in the same batch as the main artifact, otherwise they get different snapshot versions 42582: ALF-16255: CopiedFromAspectPatch fails on rules copied with a folder - Checked to make sure that cm:copiedfrom target is a cm:object before attempting a cm:original association. - Remove cm:copiedfrom aspect from source if cm:copiedfrom target is not a cm:object. 42593: ALF-16255: CopiedFromAspectPatch fails on rules copied with a folder - Corrections to log message and formatting. 42605: ALF-16231: Fixed broken IMAP unit tests 42612: Further fix for ALF-16164 Cloud monitoring of SOLR is CPU intensive due to its repeated use of the SOLR stats page - protect from dodgey JSON output 42624: ALF-14353: switch groupId to org.alfresco.enterprise, to be in sync with actual Maven deployment 42657: Fix for ALF-16359 Fix SOLR logging in production and other environments - configure in log4j-solr.properties anywhere on the solr web app classpath ... 42671: ALF-14353: fix facebook api dependency 42679: Merged V3.4-BUG-FIX to V4.1-BUG-FIX 42172: ALF-15262: Correct handling of linked rule deletion - When the last rule is removed from a folder and the ASPECT_RULES aspect is removed from its parent, we must cascade this removal to its secondary parents 42173: ALF-14400: Only site members can Edit Online (sharepoint) although the site is public and permissions allow editing for everybody - Rationalized the fix provided by Alex Malinovsky - Don't bother checking site memberships - let ACLs handle that and just check for permission to read the document 42182: Incremented version revision for 3.4.12 42243: ALF-15262: Further correction by Dmitry: use beforeRemoveAspect because beforeDeleteChildAssociation is not invoked on deletion of primary child associations 42278: ALF-12999: Correction by Alex M 42586: BDE-101: make .MD5 files suitable for easy check with md5sum -c 42627: Merged DEV to V3.4-BUG-FIX 42537: ALF-16139: Impossible to connect to CMIS via AtomPub and Web Services Activation libraries (including all Geronimo versions) have been removed because of a conflict with libraries in JBoss CXF WS installation. Also, 'javax.activation' is part of the JDK 1.6 (http://docs.oracle.com/javase/6/docs/api/javax/activation/DataHandler.html) 42677: Merged V3.4 to V3.4-BUG-FIX 42380: ALF-16220: Merged V4.1-BUG-FIX to V3.4 40590: ALF-15318: It was possible for a user with a disabled / expired account to log in via NTLM/SSO 40663: Merged DEV to V4.1-BUG-FIX 40661: ALF-15318 (part 2): It's possible to log in by disabled user (NTLM with SSO in a clustered env) The onValidateFailed() methods were moved to BaseSSOAuthenticationFilter to response with a 401 for a disabled user. 42556: ALF-15077: Site creation in Share is very very slow with over 15000 sites - Probably knock-on impact from us versioning secondary associations properly - Found old way of locating a leaf document to be ineffective as it would blow the caches (find all documents with the correct ID, then filter out the containers) - Effect was magnified when admin user was previously accessed via the explorer client and thus had an app:configurations child node, thus making admin a container and requiring its paths (e.g. zillions of nested group memberships) to be indexed - Instead, we have a new LEAFID field on leaves that we can use to efficiently locate a node to delete without hitting zillions of containers - Left backward compatible code to avoid requiring a full reindex 42557: ALF-16202: Merged V4.1-BUG-FIX to V3.4 40937: ALF-15702, ALF-15669: mmt-dependencies was messing up the SDK classpath 42566: ALF-15077: Correction to category-handling logic in container generation to fix failing unit tests 42608: Merged DEV to V3.4 42543: ALF-16248 : IE specific: It's impossible to create any event due to script error Correction for the fix for ALF-13623 to support IE8, also added clearing of 'allday' checkbox. 42622: ALF-16339: Group names incorrect in (non-site) "Manage Permissions" page - Site name was being used as the display name of all site groups! 42632: ALF-16354: Merged PATCHES/V3.4.6 to V3.4-BUG-FIX 42521: ALF-16231: Corrected LockUtils.isLockedOrReadOnly to properly handle the LOCK_EXPIRED status - Also fixed CheckOutCheckInService.checkout() to respect LOCK_EXPIRED but still disallow overwrite of unexpired WRITE_LOCKS 42522: ALF-16231: Further improvements - Renamed to isLockedAndReadOnly because that's what it means! 42644: ALF-16298: Cannot install RM amps on 4.1.1 - Passed command line arguments from shell script to mmt utility 42656: ALF-16298: Correction to DOS argument concatenation to allow multiple parameters separated by space 42664: ALF-16358: NPE detected during benchmark test. - Guarding against this in LeafScorer 42665: ALF-16360: Merged HEAD to V3.4 42440: ALF-16247: Thumbnails not rendering for PDFs with standard fonts - Because GS_LIB wasn't set on Linux and OSX 42447: ALF-16247: Thumbnails not rendering for PDFs with standard fonts - Fixes by Bitrock 42678: Merged V3.4 to V3.4-BUG-FIX (RECORD ONLY) 42244: Merged V3.4-BUG-FIX to V3.4 42172: ALF-15262: Correct handling of linked rule deletion - When the last rule is removed from a folder and the ASPECT_RULES aspect is removed from its parent, we must cascade this removal to its secondary parents 42243: ALF-15262: Further correction by Dmitry: use beforeRemoveAspect because beforeDeleteChildAssociation is not invoked on deletion of primary child associations 42279: Merged V3.4-BUG-FIX to V3.4 42278: ALF-12999: Correction by Alex M 42282: Merged V3.4-BUG-FIX to V3.4 42281: Fix for ALF-9946 Need a supported solution for switching off content indexing (FTS) -> merge only to 4.1-BUG-FIX - remove references to isIndexed property which was removed in the back port git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@42683 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/audit/alfresco-audit-access.xml | 6 +- .../bootstrap/imapSpacesTemplates.acp | Bin 21067 -> 0 bytes .../bootstrap/imapSpacesTemplates.xml | 427 ++++++ config/alfresco/bootstrap/spaces.xml | 7 + .../emailbody_texthtml_alfresco.ftl | 80 ++ .../emailbody_texthtml_alfresco_de.ftl | 80 ++ .../emailbody_texthtml_alfresco_es.ftl | 87 ++ .../emailbody_texthtml_alfresco_fr.ftl | 82 ++ .../emailbody_texthtml_alfresco_it.ftl | 81 ++ .../emailbody_texthtml_alfresco_ja.ftl | 80 ++ .../emailbody_texthtml_share.ftl | 98 ++ .../emailbody_texthtml_share_de.ftl | 98 ++ .../emailbody_texthtml_share_es.ftl | 98 ++ .../emailbody_texthtml_share_fr.ftl | 98 ++ .../emailbody_texthtml_share_it.ftl | 98 ++ .../emailbody_texthtml_share_ja.ftl | 98 ++ .../emailbody_textplain_alfresco.ftl | 27 + .../emailbody_textplain_alfresco_de.ftl | 25 + .../emailbody_textplain_alfresco_es.ftl | 25 + .../emailbody_textplain_alfresco_fr.ftl | 25 + .../emailbody_textplain_alfresco_it.ftl | 25 + .../emailbody_textplain_alfresco_ja.ftl | 29 + .../emailbody_textplain_share.ftl | 26 + .../emailbody_textplain_share_de.ftl | 26 + .../emailbody_textplain_share_es.ftl | 26 + .../emailbody_textplain_share_fr.ftl | 26 + .../emailbody_textplain_share_it.ftl | 26 + .../emailbody_textplain_share_ja.ftl | 26 + config/alfresco/import-export-context.xml | 7 +- .../messages/bootstrap-spaces.properties | 3 + .../messages/bootstrap-spaces_ru.properties | 3 + .../alfresco/patch/patch-services-context.xml | 2 +- config/alfresco/repository.properties | 3 +- .../Authentication/common-ldap-context.xml | 10 + .../ldap-ad/ldap-ad-authentication.properties | 5 +- .../ldap/ldap-authentication.properties | 5 +- config/alfresco/thumbnail-service-context.xml | 4 +- pom.xml | 14 +- .../cmis/mapping/CMISServicesImpl.java | 14 +- .../filesys/repo/LegacyFileStateDriver.java | 5 + .../opencmis/AlfrescoCmisServiceImpl.java | 17 +- .../executer/CheckOutActionExecuterTest.java | 103 ++ .../activities/ActivityPostServiceImpl.java | 15 +- .../activities/ActivityServiceImplTest.java | 124 +- .../patch/impl/CopiedFromAspectPatch.java | 23 +- .../repo/coci/CheckOutCheckInServiceImpl.java | 5 +- .../repo/content/ContentServiceImpl.java | 12 +- .../domain/activities/ActivityPostDAO.java | 37 +- .../domain/solr/NodeParametersEntity.java | 2 +- .../alfresco/repo/imap/ImapMessageTest.java | 2 +- .../repo/imap/ImapServiceImplCacheTest.java | 2 +- .../repo/imap/ImapServiceImplTest.java | 2 +- .../org/alfresco/repo/jscript/ScriptNode.java | 9 +- .../org/alfresco/repo/lock/LockUtils.java | 16 +- .../filefolder/GetChildrenCannedQuery.java | 83 +- .../GetChildrenCannedQueryFactory.java | 2 +- .../getchildren/GetChildrenCannedQuery.java | 18 +- .../GetChildrenCannedQueryTest.java | 192 ++- .../alfresco/repo/rule/RuleServiceImpl.java | 6 +- .../org/alfresco/repo/rule/RuleTypeImpl.java | 28 +- .../org/alfresco/repo/rule/RulesAspect.java | 70 +- .../OnContentUpdateRuleTrigger.java | 70 +- .../ruletrigger/OnMoveNodeRuleTrigger.java | 46 +- .../rule/ruletrigger/RuleTriggerTest.java | 393 ++--- .../impl/lucene/ADMLuceneIndexerImpl.java | 80 +- .../lucene/AbstractLuceneIndexerImpl.java | 35 - .../lucene/FilterIndexReaderByStringId.java | 34 +- .../security/person/PersonServiceImpl.java | 352 ++++- .../alfresco/repo/site/SiteServiceImpl.java | 10 +- .../RepoTransferReceiverImplTest.java | 1276 +++++++++++------ .../repo/version/VersionableAspect.java | 2 +- .../version/common/VersionHistoryImpl.java | 61 +- .../cmr/coci/CheckOutCheckInService.java | 4 +- .../service/cmr/rule/RuleService.java | 4 +- .../config/RepositoryFolderConfigBean.java | 85 +- 75 files changed, 4065 insertions(+), 1060 deletions(-) delete mode 100644 config/alfresco/bootstrap/imapSpacesTemplates.acp create mode 100644 config/alfresco/bootstrap/imapSpacesTemplates.xml create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_de.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_es.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_fr.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_it.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_ja.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_de.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_es.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_fr.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_it.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_ja.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_de.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_es.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_fr.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_it.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_ja.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_de.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_es.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_fr.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_it.ftl create mode 100644 config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_ja.ftl create mode 100644 source/java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java diff --git a/config/alfresco/audit/alfresco-audit-access.xml b/config/alfresco/audit/alfresco-audit-access.xml index 4da6bf464c..61e8457703 100644 --- a/config/alfresco/audit/alfresco-audit-access.xml +++ b/config/alfresco/audit/alfresco-audit-access.xml @@ -28,9 +28,9 @@ - - - + + + diff --git a/config/alfresco/bootstrap/imapSpacesTemplates.acp b/config/alfresco/bootstrap/imapSpacesTemplates.acp deleted file mode 100644 index 680891c37eb41ff3202a4853c0a273628777ce73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21067 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 diff --git a/config/alfresco/bootstrap/imapSpacesTemplates.xml b/config/alfresco/bootstrap/imapSpacesTemplates.xml new file mode 100644 index 0000000000..95c5e54881 --- /dev/null +++ b/config/alfresco/bootstrap/imapSpacesTemplates.xml @@ -0,0 +1,427 @@ + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for share - ${version.default} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_share.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + emailbody_textplain_share.ftl + + emailbody_textplain_share.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for explorer - ${version.default} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + emailbody_textplain_alfresco.ftl + + emailbody_textplain_alfresco.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for explorer - ${version.default} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + emailbody_texthtml_alfresco.ftl + + emailbody_texthtml_alfresco.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for share - ${version.default} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=en_US_ + emailbody_texthtml_share.ftl + + emailbody_texthtml_share.ftl + + + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for share - ${version.german} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_de.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=de + emailbody_textplain_share_de.ftl + + emailbody_textplain_share_de.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for explorer - ${version.german} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_de.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=de + emailbody_textplain_alfresco_de.ftl + + emailbody_textplain_alfresco_de.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for explorer - ${version.german} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_de.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=de + emailbody_texthtml_alfresco_de.ftl + + emailbody_texthtml_alfresco_de.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for share - ${version.german} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_de.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=de + emailbody_texthtml_share_de.ftl + + emailbody_texthtml_share_de.ftl + + + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for share - ${version.spanish} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_es.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=es + emailbody_textplain_share_es.ftl + + emailbody_textplain_share_es.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for explorer - ${version.spanish} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_es.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=es + emailbody_textplain_alfresco_es.ftl + + emailbody_textplain_alfresco_es.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for explorer - ${version.spanish} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_es.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=es + emailbody_texthtml_alfresco_es.ftl + + emailbody_texthtml_alfresco_es.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for share - ${version.spanish} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_es.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=es + emailbody_texthtml_share_es.ftl + + emailbody_texthtml_share_es.ftl + + + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for share - ${version.french} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_fr.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=fr + emailbody_textplain_share_fr.ftl + + emailbody_textplain_share_fr.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for explorer - ${version.french} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_fr.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=fr + emailbody_textplain_alfresco_fr.ftl + + emailbody_textplain_alfresco_fr.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for explorer - ${version.french} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_fr.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=fr + emailbody_texthtml_alfresco_fr.ftl + + emailbody_texthtml_alfresco_fr.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for share - ${version.french} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_fr.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=fr + emailbody_texthtml_share_fr.ftl + + emailbody_texthtml_share_fr.ftl + + + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for share - ${version.italian} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_it.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=it + emailbody_textplain_share_it.ftl + + emailbody_textplain_share_it.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for explorer - ${version.italian} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_it.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=it + emailbody_textplain_alfresco_it.ftl + + emailbody_textplain_alfresco_it.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for explorer - ${version.italian} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_it.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=it + emailbody_texthtml_alfresco_it.ftl + + emailbody_texthtml_alfresco_it.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for share - ${version.italian} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_it.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=it + emailbody_texthtml_share_it.ftl + + emailbody_texthtml_share_it.ftl + + + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for share - ${version.japanese} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_ja.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=ja + emailbody_textplain_share_ja.ftl + + emailbody_textplain_share_ja.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_textplain.description} for explorer - ${version.japanese} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_ja.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=ja + emailbody_textplain_alfresco_ja.ftl + + emailbody_textplain_alfresco_ja.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for explorer - ${version.japanese} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_ja.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=ja + emailbody_texthtml_alfresco_ja.ftl + + emailbody_texthtml_alfresco_ja.ftl + + + + + + + + + + + + true + ${spaces.imap_templates.emailbody_texthtml.description} for share - ${version.japanese} + contentUrl=classpath:alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_ja.ftl|mimetype=text/plain|size=|encoding=UTF-8|locale=ja + emailbody_texthtml_share_ja.ftl + + emailbody_texthtml_share_ja.ftl + + + + + + \ No newline at end of file diff --git a/config/alfresco/bootstrap/spaces.xml b/config/alfresco/bootstrap/spaces.xml index 32f9c10fac..67aec4fba4 100644 --- a/config/alfresco/bootstrap/spaces.xml +++ b/config/alfresco/bootstrap/spaces.xml @@ -134,6 +134,13 @@ ${spaces.user_homes.name} ${spaces.user_homes.description} + + + ${spaces.imap_attachments.name} + space-icon-default + ${spaces.imap_attachments.name} + ${spaces.imap_attachments.description} + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco.ftl new file mode 100644 index 0000000000..babd395012 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco.ftl @@ -0,0 +1,80 @@ + + + + + + + + + + +


+

Document (name): ${document.name}

+
+

Metadata

+ + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Title:${document.properties.title}
Title: 
Description:${document.properties.description}
Description: 
Creator:${document.properties.creator}
Created:${document.properties.created?datetime}
Modifier:${document.properties.modifier}
Modified:${document.properties.modified?datetime}
Size:${document.size / 1024} Kb
+
+

Content links

+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_de.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_de.ftl new file mode 100644 index 0000000000..e3a8cd9198 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_de.ftl @@ -0,0 +1,80 @@ + + + + + + + + + + +
+

Dokument (Name): ${document.name}

+
+

Metadaten

+ + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Titel:${document.properties.title}
Titel: 
Beschreibung:${document.properties.description}
Beschreibung: 
Ersteller:${document.properties.creator}
Erstellt am:${document.properties.created?datetime}
Bearbeiter:${document.properties.modifier}
Bearbeitet am:${document.properties.modified?datetime}
Größe:${document.size / 1024} Kb
+
+

Links zum Inhalt

+ + + + + + + + + + + + + + + + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_es.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_es.ftl new file mode 100644 index 0000000000..da53f8ac35 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_es.ftl @@ -0,0 +1,87 @@ + + + + + + + + + + +
+

Documento (nombre): ${document.name}

+
+

Metadatos

+ + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Título:${document.properties.title}
Título: 
Descripción:${document.properties.description}
Descripción: 
Creador:${document.properties.creator}
Creado:${document.properties.created?datetime}
Modificador:${document.properties.modifier}
Modificado:${document.properties.modified?datetime}
Tamaño:${document.size / 1024} KB
+
+

Enlaces de contenido:

+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_fr.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_fr.ftl new file mode 100644 index 0000000000..1cc6f3066c --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_fr.ftl @@ -0,0 +1,82 @@ + + + + + + + + + + +
+

Document (nom) : ${document.name}

+
+

Métadonnées

+ + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Titre :${document.properties.title}
Titre : 
Description :${document.properties.description}
Description : 
Créateur :${document.properties.creator}
Créé :${document.properties.created?datetime}
Modificateur :${document.properties.modifier}
Modifié :${document.properties.modified?datetime}
Taille :${document.size / 1024} Ko
+
+

Liens de contenu

+ + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_it.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_it.ftl new file mode 100644 index 0000000000..7cfa6510b3 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_it.ftl @@ -0,0 +1,81 @@ + + + + + + + + + + +
+

Documento (nome): ${document.name}

+
+

Metadati

+ + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Titolo:${document.properties.title}
Titolo: 
Descrizione:${document.properties.description}
Descrizione: 
Autore:${document.properties.creator}
Data di creazione:${document.properties.created?datetime}
Modificatore:${document.properties.modifier}
Data di modifica:${document.properties.modified?datetime}
Dimensioni:${document.size / 1024} Kb
+
+

Collegamenti del contenuto

+ + + + + + + + + + + + + + + + + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_ja.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_ja.ftl new file mode 100644 index 0000000000..5d7234d18f --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_alfresco_ja.ftl @@ -0,0 +1,80 @@ + + + + + + + + + + +
+

文書 (名前): ${document.name}

+
+

メタデータ

+ + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
タイトル:${document.properties.title}
タイトル: 
説明:${document.properties.description}
説明: 
作成者:${document.properties.creator}
作成日時:${document.properties.created?datetime}
修正者:${document.properties.modifier}
修正日時:${document.properties.modified?datetime}
サイズ: ${document.size / 1024} KB
+
+

コンテンツリンク

+ + + + + + + + + + + + + + + + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share.ftl new file mode 100644 index 0000000000..824b4c6f5d --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share.ftl @@ -0,0 +1,98 @@ + + + + + + + + + + +
+

Document (name): ${document.name}

+
+
+ Metadata + + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Title:${document.properties.title}
Title: 
Description:${document.properties.description}
Description: 
Creator:${document.properties.creator}
Created:${document.properties.created?datetime}
Modifier:${document.properties.modifier}
Modified:${document.properties.modified?datetime}
Size:${document.size / 1024} Kb
+
+
+ Content links + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_de.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_de.ftl new file mode 100644 index 0000000000..696f5008be --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_de.ftl @@ -0,0 +1,98 @@ + + + + + + + + + + +
+

Dokument (Name): ${document.name}

+
+
+ Metadata + + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Titel:${document.properties.title}
Titel: 
Beschreibung:${document.properties.description}
Beschreibung: 
Ersteller:${document.properties.creator}
Erstellt am:${document.properties.created?datetime}
Bearbeiter:${document.properties.modifier}
Bearbeitet am:${document.properties.modified?datetime}
Größe:${document.size / 1024} Kb
+
+
+ Links zum Inhalt + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_es.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_es.ftl new file mode 100644 index 0000000000..de2119d36b --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_es.ftl @@ -0,0 +1,98 @@ + + + + + + + + + + +
+

Documento (nombre): ${document.name}

+
+
+ Metadatos + + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Título:${document.properties.title}
Título: 
Descripción:${document.properties.description}
Descripción: 
Creador:${document.properties.creator}
Creado:${document.properties.created?datetime}
Modificador:${document.properties.modifier}
Modificado:${document.properties.modified?datetime}
Tamaño:${document.size / 1024} Kb
+
+
+ Enlaces de contenido: + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_fr.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_fr.ftl new file mode 100644 index 0000000000..5ad655c61f --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_fr.ftl @@ -0,0 +1,98 @@ + + + + + + + + + + +
+

Document (nom) : ${document.name}

+
+
+ Métadonnées + + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Titre :${document.properties.title}
Titre : 
Description :${document.properties.description}
Description : 
Créateur :${document.properties.creator}
Créé :${document.properties.created?datetime}
Modificateur :${document.properties.modifier}
Modifié :${document.properties.modified?datetime}
Taille :${document.size / 1024} Kb
+
+
+ Liens de contenu + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_it.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_it.ftl new file mode 100644 index 0000000000..9eb1ebd16d --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_it.ftl @@ -0,0 +1,98 @@ + + + + + + + + + + +
+

Documento (nome): ${document.name}

+
+
+ Metadati + + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
Titolo:${document.properties.title}
Titolo: 
Descrizione:${document.properties.description}
Descrizione: 
Autore:${document.properties.creator}
Data di creazione:${document.properties.created?datetime}
Modificatore:${document.properties.modifier}
Data di modifica:${document.properties.modified?datetime}
Dimensioni:${document.size / 1024} Kb
+
+
+ Collegamenti del contenuto + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_ja.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_ja.ftl new file mode 100644 index 0000000000..df927dfe7f --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_texthtml_share_ja.ftl @@ -0,0 +1,98 @@ + + + + + + + + + + +
+

文書 (名前): ${document.name}

+
+
+メタデータ + + <#if document.properties.title?exists> + + <#else> + + + <#if document.properties.description?exists> + + <#else> + + + + + + + +
タイトル:${document.properties.title}
タイトル: 
説明:${document.properties.description}
説明: 
作成者:${document.properties.creator}
作成日時:${document.properties.created?datetime}
修正者:${document.properties.modifier}
修正日時:${document.properties.modified?datetime}
サイズ:${document.size / 1024} Kb
+
+
+ コンテンツリンク + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco.ftl new file mode 100644 index 0000000000..155102daa6 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco.ftl @@ -0,0 +1,27 @@ +------------------------------------------------------------------------------ +Document name: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Title: ${document.properties.title} + <#else> +Title: NONE + + <#if document.properties.description?exists> +Description: ${document.properties.description} + <#else> +Description: NONE + +Creator: ${document.properties.creator} +Created: ${document.properties.created?datetime} +Modifier: ${document.properties.modifier} +Modified: ${document.properties.modified?datetime} +Size: ${document.size / 1024} Kb + + +CONTENT LINKS + +Content folder: ${contextUrl}/navigate/browse${document.parent.webdavUrl} +Content URL: ${contextUrl}${document.url} +Download URL: ${contextUrl}${document.downloadUrl} +WebDAV URL: ${contextUrl}${document.webdavUrl} diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_de.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_de.ftl new file mode 100644 index 0000000000..03487e9290 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_de.ftl @@ -0,0 +1,25 @@ +------------------------------------------------------------------------------ +Dokumentname: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Titel: ${document.properties.title} + <#else> +Titel: KEINER + + <#if document.properties.description?exists> +Beschreibung: ${document.properties.description} + <#else> +Beschreibung: KEINE + +Ersteller: ${document.properties.creator} +Erstellt am: ${document.properties.created?datetime} +Bearbeiter: ${document.properties.modifier} +Bearbeitet am: ${document.properties.modified?datetime} +Größe: ${document.size / 1024} Kb + + +LINKS ZUM INHALT + +URL zum Inhalt: ${document.shareUrl} + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_es.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_es.ftl new file mode 100644 index 0000000000..c69761574d --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_es.ftl @@ -0,0 +1,25 @@ +------------------------------------------------------------------------------ +Nombre del documento: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Título: ${document.properties.title} + <#else> +Título: NINGUNO + + <#if document.properties.description?exists> +Descripción: ${document.properties.description} + <#else> +Descripción: NINGUNA + +Creador: ${document.properties.creator} +Creado: ${document.properties.created?datetime} +Modificador: ${document.properties.modifier} +Modificado: ${document.properties.modified?datetime} +Tamaño: ${document.size / 1024} KB + + +ENLACES DE CONTENIDO + +Dirección URL de contenido: ${document.shareUrl} + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_fr.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_fr.ftl new file mode 100644 index 0000000000..8b6fbcc252 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_fr.ftl @@ -0,0 +1,25 @@ +------------------------------------------------------------------------------ +Nom du document : ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Titre : ${document.properties.title} + <#else> +Titre : AUCUN + + <#if document.properties.description?exists> +Description : ${document.properties.description} + <#else> +Description : AUCUN + +Créateur : ${document.properties.creator} +Créé : ${document.properties.created?datetime} +Modificateur : ${document.properties.modifier} +Modifié : ${document.properties.modified?datetime} +Taille : ${document.size / 1024} Ko + + +LIENS DE CONTENU + +URL de contenu : ${document.shareUrl} + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_it.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_it.ftl new file mode 100644 index 0000000000..8fa97e2386 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_it.ftl @@ -0,0 +1,25 @@ +------------------------------------------------------------------------------ +Nome documento: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Titolo: ${document.properties.title} + <#else> +Titolo: NESSUNO + + <#if document.properties.description?exists> +Descrizione: ${document.properties.description} + <#else> +Descrizione: NESSUNA + +Autore: ${document.properties.creator} +Data di creazione: ${document.properties.created?datetime} +Modificatore: ${document.properties.modifier} +Data di modifica: ${document.properties.modified?datetime} +Dimensioni: ${document.size / 1024} Kb + + +COLLEGAMENTI DEL CONTENUTO + +URL del contenuto: ${document.shareUrl} + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_ja.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_ja.ftl new file mode 100644 index 0000000000..f2670c8811 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_alfresco_ja.ftl @@ -0,0 +1,29 @@ +------------------------------------------------------------------------------ +ドキュメント名: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +タイトル: ${document.properties.title} + <#else> +タイトル: なし + + <#if document.properties.description?exists> +説明: ${document.properties.description} + <#else> +説明: なし + +作成者: ${document.properties.creator} +作成日時: ${document.properties.created?datetime} +修正者: ${document.properties.modifier} +修正日時: ${document.properties.modified?datetime} +サイズ: ${document.size / 1024} KB + + +コンテンツリンク + +コンテンツ フォルダー: ${contextUrl}/navigate/browse${document.parent.webdavUrl} +コンテンツ URL: ${contextUrl}${document.url} +ダウンロード URL: ${contextUrl}${document.downloadUrl} +WebDAV URL: ${contextUrl}${document.webdavUrl} + + diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share.ftl new file mode 100644 index 0000000000..6061e54334 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share.ftl @@ -0,0 +1,26 @@ +------------------------------------------------------------------------------ +Document name: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Title: ${document.properties.title} + <#else> +Title: NONE + + <#if document.properties.description?exists> +Description: ${document.properties.description} + <#else> +Description: NONE + +Creator: ${document.properties.creator} +Created: ${document.properties.created?datetime} +Modifier: ${document.properties.modifier} +Modified: ${document.properties.modified?datetime} +Size: ${document.size / 1024} Kb + + +CONTENT LINKS + +Content folder: ${shareContextUrl}/page/site/${parentPathFromSites} +Content URL: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name} +Download URL: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name}?a=true diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_de.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_de.ftl new file mode 100644 index 0000000000..34dc76ab70 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_de.ftl @@ -0,0 +1,26 @@ +------------------------------------------------------------------------------ +Dokumentname: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Titel: ${document.properties.title} + <#else> +Titel: KEINER + + <#if document.properties.description?exists> +Beschreibung: ${document.properties.description} + <#else> +Beschreibung: KEINE + +Ersteller: ${document.properties.creator} +Erstellt am: ${document.properties.created?datetime} +Bearbeiter: ${document.properties.modifier} +Bearbeitet am: ${document.properties.modified?datetime} +Größe: ${document.size / 1024} Kb + + +Links zum Inhalt + +Dokumenten Ordner: ${shareContextUrl}/page/site/${parentPathFromSites} +URL zum Inhalt: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name} +Download URL: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name}?a=true diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_es.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_es.ftl new file mode 100644 index 0000000000..f3b73a4f56 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_es.ftl @@ -0,0 +1,26 @@ +------------------------------------------------------------------------------ +Nombre del documento: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Título: ${document.properties.title} + <#else> +Título: NINGUNO + + <#if document.properties.description?exists> +Descripción: ${document.properties.description} + <#else> +Descripción: NINGUNA + +Creador: ${document.properties.creator} +Creado: ${document.properties.created?datetime} +Modificador: ${document.properties.modifier} +Modificado: ${document.properties.modified?datetime} +Tamaño: ${document.size / 1024} Kb + + +Enlaces de contenido + +Carpeta de contenido: ${shareContextUrl}/page/site/${parentPathFromSites} +Dirección URL de contenido: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name} +Dirección URL de descarga: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name}?a=true diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_fr.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_fr.ftl new file mode 100644 index 0000000000..6710d95784 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_fr.ftl @@ -0,0 +1,26 @@ +------------------------------------------------------------------------------ +Nom du document : ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Titre: ${document.properties.title} + <#else> +Titre: AUCUN + + <#if document.properties.description?exists> +Description: ${document.properties.description} + <#else> +Description: AUCUN + +Créateur: ${document.properties.creator} +Créé: ${document.properties.created?datetime} +Modificateur: ${document.properties.modifier} +Modifié: ${document.properties.modified?datetime} +Taille: ${document.size / 1024} Kb + + +Liens de contenu + +Dossier du contenu : ${shareContextUrl}/page/site/${parentPathFromSites} +URL de contenu : ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name} +Adresse de téléchargement : ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name}?a=true \ No newline at end of file diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_it.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_it.ftl new file mode 100644 index 0000000000..09053b3a93 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_it.ftl @@ -0,0 +1,26 @@ +------------------------------------------------------------------------------ +Nome documento: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +Titolo: ${document.properties.title} + <#else> +Titolo: NESSUNO + + <#if document.properties.description?exists> +Descrizione: ${document.properties.description} + <#else> +Descrizione: NESSUNO + +Autore: ${document.properties.creator} +Data di creazione: ${document.properties.created?datetime} +Modificatore: ${document.properties.modifier} +Data di modifica: ${document.properties.modified?datetime} +Dimensioni: ${document.size / 1024} Kb + + +Collegamenti del contenuto + +Cartella del contenuto: ${shareContextUrl}/page/site/${parentPathFromSites} +URL del contenuto: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name} +URL di download: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name}?a=true diff --git a/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_ja.ftl b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_ja.ftl new file mode 100644 index 0000000000..572cb43959 --- /dev/null +++ b/config/alfresco/imap/imapSpacesTemplates/emailbody_textplain_share_ja.ftl @@ -0,0 +1,26 @@ +------------------------------------------------------------------------------ +ドキュメント名: ${document.name} +------------------------------------------------------------------------------ + + <#if document.properties.title?exists> +タイトル: ${document.properties.title} + <#else> +タイトル: なし + + <#if document.properties.description?exists> +説明: ${document.properties.description} + <#else> +説明: なし + +作成者: ${document.properties.creator} +作成日時: ${document.properties.created?datetime} +修正者: ${document.properties.modifier} +修正日時: ${document.properties.modified?datetime} +サイズ: ${document.size / 1024} Kb + + +コンテンツリンク + +コンテンツ フォルダー: ${shareContextUrl}/page/site/${parentPathFromSites} +コンテンツURL: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name} +ダウンロード URL: ${shareContextUrl}/proxy/alfresco/api/node/content/${document.storeType}/${document.storeId}/${document.id}/${document.name}?a=true diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index 8ed9cadca5..be16752543 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -396,6 +396,7 @@ ${spaces.transfer_groups.childname} ${spaces.transfer_temp.childname} ${spaces.inbound_transfer_records.childname} + ${spaces.imap_attachments.childname} ${spaces.imap_templates.childname} ${spaces.emailActions.childname} ${spaces.searchAction.childname} @@ -684,16 +685,14 @@ alfresco/messages/bootstrap-spaces - /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname} - alfresco/bootstrap/imapSpacesTemplates.acp + alfresco/bootstrap/imapSpacesTemplates.xml alfresco/messages/bootstrap-spaces - ${publishing.root.path} alfresco/bootstrap/publishingRootFolder.xml diff --git a/config/alfresco/messages/bootstrap-spaces.properties b/config/alfresco/messages/bootstrap-spaces.properties index c6b7eee3db..a92e5b146c 100644 --- a/config/alfresco/messages/bootstrap-spaces.properties +++ b/config/alfresco/messages/bootstrap-spaces.properties @@ -6,6 +6,9 @@ spaces.company_home.description=The company root space spaces.dictionary.name=Data Dictionary spaces.dictionary.description=User managed definitions +spaces.imap_attachments.name=Imap Attachments +spaces.imap_attachments.description=Imap Attachments + spaces.imapConfig.name=Imap Configs spaces.imapConfig.description=Imap Configs diff --git a/config/alfresco/messages/bootstrap-spaces_ru.properties b/config/alfresco/messages/bootstrap-spaces_ru.properties index d2148a2241..0b0322d435 100755 --- a/config/alfresco/messages/bootstrap-spaces_ru.properties +++ b/config/alfresco/messages/bootstrap-spaces_ru.properties @@ -6,6 +6,9 @@ spaces.company_home.description=\u041A\u043E\u0440\u043D\u0435\u0432\u043E\u0435 spaces.dictionary.name=\u0421\u043B\u043E\u0432\u0430\u0440\u044C \u0434\u0430\u043D\u043D\u044B\u0445 spaces.dictionary.description=\u0423\u043F\u0440\u0430\u0432\u043B\u044F\u0435\u043C\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u044F +spaces.imap_attachments.name=\u0412\u043B\u043E\u0436\u0435\u043D\u0438\u044F IMAP +spaces.imap_attachments.description=\u0412\u043B\u043E\u0436\u0435\u043D\u0438\u044F IMAP + spaces.imapConfig.name=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 Imap spaces.imapConfig.description=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 Imap diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 1f67fa5b3f..7c51c10819 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -2701,7 +2701,7 @@ /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.imapConfig.childname}/${spaces.imap_templates.childname} - alfresco/bootstrap/imapSpacesTemplates.acp + alfresco/bootstrap/imapSpacesTemplates.xml alfresco/messages/bootstrap-spaces diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 8a3b6054b7..c59a4c0760 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -397,6 +397,7 @@ spaces.company_home.childname=app:company_home spaces.guest_home.childname=app:guest_home spaces.dictionary.childname=app:dictionary spaces.templates.childname=app:space_templates +spaces.imap_attachments.childname=cm:Imap Attachments spaces.imapConfig.childname=app:imap_configs spaces.imap_templates.childname=app:imap_templates spaces.scheduled_actions.childname=cm:Scheduled Actions @@ -742,7 +743,7 @@ imap.server.attachments.extraction.enabled=true imap.attachments.mode=SEPARATE imap.attachments.folder.store=${spaces.store} imap.attachments.folder.rootPath=/${spaces.company_home.childname} -imap.attachments.folder.folderPath=Imap Attachments +imap.attachments.folder.folderPath=${spaces.imap_attachments.childname} # Activities Feed - refer to subsystem diff --git a/config/alfresco/subsystems/Authentication/common-ldap-context.xml b/config/alfresco/subsystems/Authentication/common-ldap-context.xml index bbd9118ae1..a9ab794413 100644 --- a/config/alfresco/subsystems/Authentication/common-ldap-context.xml +++ b/config/alfresco/subsystems/Authentication/common-ldap-context.xml @@ -111,6 +111,11 @@ ${ldap.authentication.java.naming.security.authentication} + + + ${ldap.authentication.java.naming.read.timeout} + + follow @@ -160,6 +165,11 @@ true + + + ${ldap.authentication.java.naming.read.timeout} + + follow diff --git a/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties b/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties index f9fa167220..d98bc9cf77 100644 --- a/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties +++ b/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties @@ -117,4 +117,7 @@ ldap.synchronization.personType=user ldap.synchronization.groupMemberAttributeName=member # If true progress estimation is enabled. When enabled, the user query has to be run twice in order to count entries. -ldap.synchronization.enableProgressEstimation=true \ No newline at end of file +ldap.synchronization.enableProgressEstimation=true + +# Requests timeout, in miliseconds, use 0 for none (default) +ldap.authentication.java.naming.read.timeout=0 \ No newline at end of file diff --git a/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties b/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties index 882eb80e9a..3fde29c01e 100644 --- a/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties +++ b/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties @@ -123,4 +123,7 @@ ldap.synchronization.personType=inetOrgPerson ldap.synchronization.groupMemberAttributeName=member # If true progress estimation is enabled. When enabled, the user query has to be run twice in order to count entries. -ldap.synchronization.enableProgressEstimation=true \ No newline at end of file +ldap.synchronization.enableProgressEstimation=true + +# Requests timeout, in miliseconds, use 0 for none (default) +ldap.authentication.java.naming.read.timeout=0 \ No newline at end of file diff --git a/config/alfresco/thumbnail-service-context.xml b/config/alfresco/thumbnail-service-context.xml index e8bcc6f4b7..191861733e 100644 --- a/config/alfresco/thumbnail-service-context.xml +++ b/config/alfresco/thumbnail-service-context.xml @@ -131,8 +131,8 @@ - - + + diff --git a/pom.xml b/pom.xml index 871f4abeff..fe22e0eab1 100644 --- a/pom.xml +++ b/pom.xml @@ -8,14 +8,14 @@ Alfresco Repository alfresco-parent - org.alfresco + org.alfresco.enterprise 4.2-SNAPSHOT ../../pom-experimental.xml - org.alfresco + org.alfresco.enterprise alfresco-data-model ${project.version} @@ -30,17 +30,17 @@ - org.alfresco + org.alfresco.enterprise alfresco-deployment ${project.version} - org.alfresco + org.alfresco.enterprise alfresco-jlan ${project.version} - org.alfresco + org.alfresco.enterprise alfresco-mbeans ${project.version} @@ -723,14 +723,14 @@ test - org.alfresco + org.alfresco.enterprise alfresco-deployment ${project.version} tests test - org.alfresco + org.alfresco.enterprise alfresco-data-model ${project.version} tests diff --git a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java index d75280b113..0095f8bb40 100644 --- a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java +++ b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -669,7 +669,17 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware, CMISPropertyDefinition propDef = cmisDictionaryService.findPropertyByQueryName(sort[0]); if (propDef != null) { - QName sortProp = propDef.getPropertyAccessor().getMappedProperty(); + QName sortProp = null; + if (propDef.getPropertyId().getId().equals(CMISDictionaryModel.PROP_BASE_TYPE_ID)) + { + // special-case (see also ALF-13968) - for getChildren, using "cmis:baseTypeId" allows sorting of folders first and vice-versa (cmis:folder <-> cmis:document) + sortProp = GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER; + } + else + { + sortProp = propDef.getPropertyAccessor().getMappedProperty(); + } + if (sortProp != null) { boolean sortAsc = (sort.length == 1) || sort[1].equalsIgnoreCase("asc"); diff --git a/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java b/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java index 3dd4f32280..e2279e3ed6 100644 --- a/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java +++ b/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java @@ -327,6 +327,7 @@ public class LegacyFileStateDriver implements ExtendedDiskInterface logger.debug("close file, release access token:" + token); } cache.releaseFileAccess(fstate, token); + } if(fstate.getOpenCount() == 0 ) @@ -337,6 +338,10 @@ public class LegacyFileStateDriver implements ExtendedDiskInterface fstate.updateChangeDateTime(0); fstate.updateModifyDateTime(0); } + + // Clear the access token + + param.setAccessToken( null); } } catch(IOException ie) diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java index 181a0f75b3..68ab783b7b 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -39,6 +39,7 @@ import javax.servlet.http.HttpServletRequest; import net.sf.acegisecurity.Authentication; +import org.alfresco.cmis.CMISDictionaryModel; import org.alfresco.cmis.CMISInvalidArgumentException; import org.alfresco.model.ContentModel; import org.alfresco.opencmis.dictionary.CMISNodeInfo; @@ -500,14 +501,24 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr for (int i = 0; i < len; i++) { String[] sort = parts[i].split(" +"); - + if (sort.length > 0) { PropertyDefinitionWrapper propDef = connector.getOpenCMISDictionaryService() .findPropertyByQueryName(sort[0]); if (propDef != null) { - QName sortProp = propDef.getPropertyAccessor().getMappedProperty(); + QName sortProp = null; + if (propDef.getPropertyId().equals(CMISDictionaryModel.PROP_BASE_TYPE_ID)) + { + // special-case (see also ALF-13968) - for getChildren, using "cmis:baseTypeId" allows sorting of folders first and vice-versa (cmis:folder <-> cmis:document) + sortProp = GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER; + } + else + { + sortProp = propDef.getPropertyAccessor().getMappedProperty(); + } + if (sortProp != null) { boolean sortAsc = (sort.length == 1) || sort[1].equalsIgnoreCase("asc"); diff --git a/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java b/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java new file mode 100644 index 0000000000..64d242e3e7 --- /dev/null +++ b/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2012 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.action.executer; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ActionImpl; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +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.namespace.QName; +import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.GUID; + +/** + * Tests checkout using action executer + */ +public class CheckOutActionExecuterTest extends BaseSpringTest +{ + private NodeService nodeService; + private CheckOutCheckInService checkOutCheckInService; + + /** + * The add features action executer + */ + private CheckOutActionExecuter executer; + + private StoreRef testStoreRef; + private NodeRef rootNodeRef; + private NodeRef nodeRefContent; + private NodeRef nodeRefFolder; + + /** + * Id used to identify the test action created + */ + private final static String ID = GUID.generate(); + + @Override + protected void onSetUpInTransaction() throws Exception + { + this.nodeService = (NodeService) this.applicationContext.getBean("nodeService"); + + this.checkOutCheckInService = (CheckOutCheckInService) this.applicationContext.getBean("checkOutCheckInService"); + + AuthenticationComponent authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent"); + authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); + + // Create the store and get the root node + this.testStoreRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + this.rootNodeRef = this.nodeService.getRootNode(this.testStoreRef); + + // Create 'content' the node used for tests + this.nodeRefContent = this.nodeService.createNode( + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}contenttestnode"), + ContentModel.TYPE_CONTENT).getChildRef(); + + // Create 'folder' the node used for tests + this.nodeRefFolder = this.nodeService.createNode( + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}foldertestnode"), + ContentModel.TYPE_FOLDER).getChildRef(); + + // Get the executer instance + this.executer = (CheckOutActionExecuter) this.applicationContext.getBean(CheckOutActionExecuter.NAME); + } + + /** + * Test execution + */ + public void testExecution() + { + // Execute the action + ActionImpl action = new ActionImpl(null, ID, CheckOutActionExecuter.NAME, null); + + // Execute check out action for 'content' node + this.executer.execute(action, this.nodeRefContent); + // Execute check out action for 'folder' node + this.executer.execute(action, this.nodeRefFolder); + + assertNotNull(this.checkOutCheckInService.getWorkingCopy(this.nodeRefContent)); + assertNull(this.checkOutCheckInService.getWorkingCopy(this.nodeRefFolder)); + } +} diff --git a/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java index 2fcb167c1f..ae9b3f5503 100644 --- a/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -41,6 +41,7 @@ import org.springframework.extensions.surf.util.ParameterCheck; * Activity Post Service Implementation * * @author janv + * @since 3.0 */ public class ActivityPostServiceImpl implements ActivityPostService { @@ -188,6 +189,17 @@ public class ActivityPostServiceImpl implements ActivityPostService activityData = jo.toString(); } checkNodeRef(jo); + + // ALF-10362 - belts-and-braces (note: Share sets "title" from cm:name) + if (jo.has(PostLookup.JSON_TITLE)) + { + String title = jo.getString(PostLookup.JSON_TITLE); + if (title.length() > ActivityPostDAO.MAX_LEN_NAME) + { + jo.put(PostLookup.JSON_TITLE, title.substring(0, 255)); + activityData = jo.toString(); + } + } } } catch (JSONException e) @@ -196,7 +208,6 @@ public class ActivityPostServiceImpl implements ActivityPostService // According to test data in org/alfresco/repo/activities/script/test_activityService.js // invalid JSON should be OK. } - if (activityData.length() > ActivityPostDAO.MAX_LEN_ACTIVITY_DATA) { diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java b/source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java index f2e60ff008..3892bf6948 100644 --- a/source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java +++ b/source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,9 +18,14 @@ */ package org.alfresco.repo.activities; +import java.util.Arrays; import java.util.HashMap; import java.util.List; +import junit.framework.TestCase; + +import org.alfresco.repo.domain.activities.ActivityPostDAO; +import org.alfresco.repo.domain.activities.ActivityPostEntity; import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.activities.ActivityService; @@ -31,58 +36,66 @@ import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; -import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; /** * Activity Service Implementation unit test * * @author janv + * @since 3.0 */ -public class ActivityServiceImplTest extends BaseSpringTest +public class ActivityServiceImplTest extends TestCase { + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + private ActivityService activityService; private ScriptService scriptService; private MutableAuthenticationService authenticationService; private SiteService siteService; + private ActivityPostDAO postDAO; private static final String ADMIN_PW = "admin"; private static final String USER_UN = "bob"; private static final String USER_PW = "bob"; - protected void onSetUpInTransaction() throws Exception + private static final String TEST_RUN_ID = ""+System.currentTimeMillis(); + + @Override + protected void setUp() throws Exception { - super.onSetUpInTransaction(); + activityService = (ActivityService)ctx.getBean("activityService"); + scriptService = (ScriptService)ctx.getBean("ScriptService"); + siteService = (SiteService)ctx.getBean("SiteService"); - // Get the required services - this.activityService = (ActivityService)this.applicationContext.getBean("activityService"); - this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService"); - this.siteService = (SiteService)this.applicationContext.getBean("SiteService"); + postDAO = (ActivityPostDAO)ctx.getBean("postDAO"); - this.authenticationService = (MutableAuthenticationService)applicationContext.getBean("authenticationService"); + authenticationService = (MutableAuthenticationService)ctx.getBean("AuthenticationService"); authenticationService.authenticate(AuthenticationUtil.getAdminUserName(), ADMIN_PW.toCharArray()); } - protected void onTearDownInTransaction() throws Exception + @Override + protected void tearDown() throws Exception { authenticationService.clearCurrentSecurityContext(); } - + public void testPostValidActivities() throws Exception { - this.activityService.postActivity("org.alfresco.testActivityType1", null, null, ""); + activityService.postActivity("org.alfresco.testActivityType1", null, null, ""); - this.activityService.postActivity("org.alfresco.testActivityType2", "", "", ""); + activityService.postActivity("org.alfresco.testActivityType2", "", "", ""); - this.activityService.postActivity("org.alfresco.testActivityType3", "site1", "appToolA", "{ \"var1\" : \"val1\" }"); + activityService.postActivity("org.alfresco.testActivityType3", "site1", "appToolA", "{ \"var1\" : \"val1\" }"); } public void testPostInvalidActivities() throws Exception { try { - this.activityService.postActivity("", "", "",(NodeRef) null, ""); + activityService.postActivity("", "", "",(NodeRef) null, ""); fail("invalid post activity"); } catch (IllegalArgumentException iae) @@ -92,7 +105,7 @@ public class ActivityServiceImplTest extends BaseSpringTest try { - this.activityService.postActivity("", "", "", ""); + activityService.postActivity("", "", "", ""); fail("invalid post activity"); } catch (IllegalArgumentException iae) @@ -102,56 +115,59 @@ public class ActivityServiceImplTest extends BaseSpringTest try { - this.activityService.postActivity("org.alfresco.testActivityType1", "", "", "{ \"nodeRef\" : \"notfound\" }"); + activityService.postActivity("org.alfresco.testActivityType1", "", "", "{ \"nodeRef\" : \"notfound\" }"); fail("invalid post activity: bad nodeRef"); } catch (IllegalArgumentException iae) { assertTrue(iae.getMessage().contains("Invalid node ref: notfound")); } - } - + } + public void testGetEmptySiteFeed() throws Exception { - authenticationService.clearCurrentSecurityContext(); - if(! authenticationService.authenticationExists(USER_UN)) { authenticationService.createAuthentication(USER_UN, USER_PW.toCharArray()); } + + authenticationService.clearCurrentSecurityContext(); + authenticationService.authenticate(USER_UN, USER_PW.toCharArray()); - siteService.createSite("mypreset", "emptySite", "empty site title", "empty site description", SiteVisibility.PUBLIC); + String siteId = "emptySite-"+TEST_RUN_ID; + siteService.createSite("mypreset", siteId, "empty site title", "empty site description", SiteVisibility.PUBLIC); - List siteFeedEntries = this.activityService.getSiteFeedEntries("emptySite", "json"); + List siteFeedEntries = activityService.getSiteFeedEntries(siteId, "json"); assertNotNull(siteFeedEntries); assertTrue(siteFeedEntries.isEmpty()); + siteService.deleteSite(siteId); } public void testGetEmptyUserFeed() throws Exception { - List userFeedEntries = this.activityService.getUserFeedEntries("unknown user", "a format", null); + List userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = this.activityService.getUserFeedEntries("unknown user", "a format", "some site"); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site"); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = this.activityService.getUserFeedEntries("unknown user", "a format", "some site", true, false, null, null); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site", true, false, null, null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = this.activityService.getUserFeedEntries("unknown user", "a format", "some site", false, true, null, null); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site", false, true, null, null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = this.activityService.getUserFeedEntries("unknown user", "a format", "some site", true, true, null, null); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site", true, true, null, null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); @@ -171,47 +187,63 @@ public class ActivityServiceImplTest extends BaseSpringTest public void testFeedControls() throws Exception { - List feedControls = this.activityService.getFeedControls(USER_UN); + List feedControls = activityService.getFeedControls(USER_UN); assertNotNull(feedControls); assertTrue(feedControls.isEmpty()); - - authenticationService.clearCurrentSecurityContext(); if(! authenticationService.authenticationExists(USER_UN)) { authenticationService.createAuthentication(USER_UN, USER_PW.toCharArray()); } + + authenticationService.clearCurrentSecurityContext(); + authenticationService.authenticate(USER_UN, USER_PW.toCharArray()); - feedControls = this.activityService.getFeedControls(); + feedControls = activityService.getFeedControls(); assertNotNull(feedControls); assertTrue(feedControls.isEmpty()); - assertFalse(this.activityService.existsFeedControl(new FeedControl("mySite1", "appTool1"))); + assertFalse(activityService.existsFeedControl(new FeedControl("mySite1", "appTool1"))); - this.activityService.setFeedControl(new FeedControl("mySite1", null)); - this.activityService.setFeedControl(new FeedControl("mySite1", "appTool1")); - this.activityService.setFeedControl(new FeedControl(null, "appTool2")); + activityService.setFeedControl(new FeedControl("mySite1", null)); + activityService.setFeedControl(new FeedControl("mySite1", "appTool1")); + activityService.setFeedControl(new FeedControl(null, "appTool2")); - feedControls = this.activityService.getFeedControls(); + feedControls = activityService.getFeedControls(); assertEquals(3, feedControls.size()); - feedControls = this.activityService.getFeedControls(USER_UN); + feedControls = activityService.getFeedControls(USER_UN); assertEquals(3, feedControls.size()); - assertTrue(this.activityService.existsFeedControl(new FeedControl("mySite1", "appTool1"))); + assertTrue(activityService.existsFeedControl(new FeedControl("mySite1", "appTool1"))); - this.activityService.unsetFeedControl(new FeedControl("mySite1", "appTool1")); + activityService.unsetFeedControl(new FeedControl("mySite1", "appTool1")); - assertFalse(this.activityService.existsFeedControl(new FeedControl("mySite1", "appTool1"))); + assertFalse(activityService.existsFeedControl(new FeedControl("mySite1", "appTool1"))); - feedControls = this.activityService.getFeedControls(); + feedControls = activityService.getFeedControls(); assertEquals(2, feedControls.size()); - this.activityService.unsetFeedControl(new FeedControl("mySite1", null)); - this.activityService.unsetFeedControl(new FeedControl(null, "appTool2")); + activityService.unsetFeedControl(new FeedControl("mySite1", null)); + activityService.unsetFeedControl(new FeedControl(null, "appTool2")); - feedControls = this.activityService.getFeedControls(); + feedControls = activityService.getFeedControls(); assertEquals(0, feedControls.size()); } + + public void testLongName_ALF_10362() throws Exception + { + byte [] namePattern = new byte[1024]; + Arrays.fill(namePattern, (byte) 'A'); + + ActivityPostEntity params = new ActivityPostEntity(); + params.setStatus(ActivityPostEntity.STATUS.PENDING.toString()); + + int cnt = postDAO.selectPosts(params).size(); + + activityService.postActivity("org.alfresco.testActivityType4", "site2", "appToolA", "{\"title\":\"" + new String(namePattern, "UTF-8") + "\"}"); + + assertEquals(cnt+1, postDAO.selectPosts(params).size()); + } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java index 8dd4b04140..6fcab4f8e6 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java @@ -448,13 +448,26 @@ public class CopiedFromAspectPatch extends AbstractPatch } else { - if (logger.isDebugEnabled()) + QName sourceTypeQName = nodeService.getType(sourceNodeRef); + // cm:copiedfrom target must be a cm:object + if (dictionaryService.isSubClass(sourceTypeQName, ContentModel.TYPE_CMOBJECT)) { - logger.debug("\tP: Adding association cm:original: " + nodePair); + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Adding association cm:original: " + nodePair); + } + writeLine(file, "Adding association cm:original: " + nodePair); + nodeService.createAssociation(nodeRef, sourceNodeRef, ContentModel.ASSOC_ORIGINAL); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Removing incompatible aspect cm:copiedfrom " + nodePair); + } + writeLine(file, "Removing incompatible aspect cm:copiedfrom " + nodePair); + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM); } - writeLine(file, "Adding association cm:original: " + nodePair); - // Create the association - nodeService.createAssociation(nodeRef, sourceNodeRef, ContentModel.ASSOC_ORIGINAL); } } if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index c6f78684df..df4ba6d8ea 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -34,7 +34,6 @@ import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCheckOut; import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCancelCheckOut; import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn; import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut; -import org.alfresco.repo.lock.LockUtils; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.PolicyComponent; @@ -42,6 +41,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.coci.CheckOutCheckInServiceException; import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.lock.NodeLockedException; import org.alfresco.service.cmr.lock.UnableToReleaseLockException; @@ -377,7 +377,8 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // It is not enough to check LockUtils.isLockedOrReadOnly in case when the same user does offline and online edit (for instance in two open browsers). In this case we get // set ContentModel.ASPECT_LOCKABLE and LockType.WRITE_LOCK. So, here we have to check following - if (lockService.getLockType(nodeRef) == LockType.WRITE_LOCK) + LockStatus lockStatus = lockService.getLockStatus(nodeRef); + if (lockStatus != LockStatus.NO_LOCK && lockStatus != LockStatus.LOCK_EXPIRED) { throw new NodeLockedException(nodeRef); } diff --git a/source/java/org/alfresco/repo/content/ContentServiceImpl.java b/source/java/org/alfresco/repo/content/ContentServiceImpl.java index ca9edefda2..1c914f7da0 100644 --- a/source/java/org/alfresco/repo/content/ContentServiceImpl.java +++ b/source/java/org/alfresco/repo/content/ContentServiceImpl.java @@ -295,7 +295,17 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa } // Check for new content - isNewContent = !hasContentBefore && hasContentAfter; + isNewContent = isNewContent || !hasContentBefore && hasContentAfter; + + // Make it clear when there's no content before or after + if (!hasContentBefore) + { + beforeValue = null; + } + if (!hasContentAfter) + { + afterValue = null; + } // So debug ... if (logger.isDebugEnabled()) diff --git a/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java b/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java index b68ab3792e..28d060f10e 100644 --- a/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java +++ b/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java @@ -1,19 +1,19 @@ /* - * 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 + * Copyright (C) 2005-2012 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.activities; @@ -24,15 +24,20 @@ import java.util.List; /** * Interface for activity post DAO service + * + * @author janv + * @since 3.0 */ public interface ActivityPostDAO extends ActivitiesDAO { public static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id public static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network public static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type - public static final int MAX_LEN_ACTIVITY_DATA = 4000; // needs to match schema: activity_data + public static final int MAX_LEN_ACTIVITY_DATA = 1024; // needs to match schema: activity_data public static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool + public static final int MAX_LEN_NAME = 255; // eg. filename + public List selectPosts(ActivityPostEntity activityPost) throws SQLException; public Long getMaxActivitySeq() throws SQLException; diff --git a/source/java/org/alfresco/repo/domain/solr/NodeParametersEntity.java b/source/java/org/alfresco/repo/domain/solr/NodeParametersEntity.java index 37ed529498..a74cadd7d3 100644 --- a/source/java/org/alfresco/repo/domain/solr/NodeParametersEntity.java +++ b/source/java/org/alfresco/repo/domain/solr/NodeParametersEntity.java @@ -48,7 +48,7 @@ public class NodeParametersEntity extends NodeParameters */ public NodeParametersEntity(QNameDAO qnameDAO) { - Pair qnamePair = qnameDAO.getOrCreateQName(ContentModel.PROP_ORIGINAL_ID); + Pair qnamePair = qnameDAO.getQName(ContentModel.PROP_ORIGINAL_ID); this.setOriginalIdPropQNameId(qnamePair == null ? -1 : qnamePair.getFirst()); } diff --git a/source/java/org/alfresco/repo/imap/ImapMessageTest.java b/source/java/org/alfresco/repo/imap/ImapMessageTest.java index a31888d09e..207eaf54e0 100644 --- a/source/java/org/alfresco/repo/imap/ImapMessageTest.java +++ b/source/java/org/alfresco/repo/imap/ImapMessageTest.java @@ -200,7 +200,7 @@ public class ImapMessageTest extends TestCase RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean(); imapHome.setStore(storePath); imapHome.setRootPath(companyHomePathInStore); - imapHome.setFolderPath(IMAP_FOLDER_NAME); + imapHome.setFolderPath(NamespaceService.CONTENT_MODEL_PREFIX + ":" + IMAP_FOLDER_NAME); imapServiceImpl.setImapHome(imapHome); diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java b/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java index c225d73ad4..cc416f472f 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java @@ -91,7 +91,7 @@ public class ImapServiceImplCacheTest extends TestCase RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean(); imapHome.setStore(storePath); imapHome.setRootPath(companyHomePathInStore); - imapHome.setFolderPath(TEST_IMAP_FOLDER_NAME); + imapHome.setFolderPath(NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME); imapServiceImpl.setImapHome(imapHome); // Starting IMAP diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java index 7e0af369cd..829a43c428 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java @@ -176,7 +176,7 @@ public class ImapServiceImplTest extends TestCase RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean(); imapHome.setStore(storePath); imapHome.setRootPath(companyHomePathInStore); - imapHome.setFolderPath(TEST_IMAP_FOLDER_NAME); + imapHome.setFolderPath(NamespaceService.CONTENT_MODEL_PREFIX + ":" + TEST_IMAP_FOLDER_NAME); imapServiceImpl.setImapHome(imapHome); // Starting IMAP diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java index 7104a7cd2a..28a578f8c5 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptNode.java +++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java @@ -51,6 +51,7 @@ import org.alfresco.repo.action.executer.TransformActionExecuter; import org.alfresco.repo.content.transform.UnimportantTransformException; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; import org.alfresco.repo.model.filefolder.FileFolderServiceImpl.InvalidTypeException; +import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; @@ -680,10 +681,14 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider ignoreTypeQNames.add(createQName(ignoreTypes.toString())); } - List> sortProps = null; // note: null sortProps => get all in default sort order + // ALF-13968 - sort folders before files (for Share) - TODO should be optional sort param + List> sortProps = new ArrayList>(2); + if ((sortProp == null) || (! sortProp.equals(GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE.getLocalName()))) + { + sortProps.add(new Pair(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, false)); + } if (sortProp != null) { - sortProps = new ArrayList>(1); sortProps.add(new Pair(createQName(sortProp), sortAsc)); } diff --git a/source/java/org/alfresco/repo/lock/LockUtils.java b/source/java/org/alfresco/repo/lock/LockUtils.java index 5bf37fbb87..f93b46f8e0 100644 --- a/source/java/org/alfresco/repo/lock/LockUtils.java +++ b/source/java/org/alfresco/repo/lock/LockUtils.java @@ -27,17 +27,25 @@ public class LockUtils { /** - * Indicates if the node is unlocked or the current user has a WRITE_LOCK

+ * Indicates if the node is locked AND it's not a WRITE_LOCK for the current user.

* * Ideally this would be a new method on the lockService, but cannot do this at the moment, * as this method is being added as part of a hot fix, so a public service cannot change * as the RM AMP might be installed and it has its own security context which would also need * to reflect this change. */ - public static boolean isLockedOrReadOnly(NodeRef nodeRef, LockService lockService) + public static boolean isLockedAndReadOnly(NodeRef nodeRef, LockService lockService) { LockStatus lockStatus = lockService.getLockStatus(nodeRef); - LockType lockType = lockService.getLockType(nodeRef); - return ! (lockStatus == LockStatus.NO_LOCK || (lockStatus == LockStatus.LOCK_OWNER && lockType == LockType.WRITE_LOCK)); + switch (lockStatus) + { + case NO_LOCK: + case LOCK_EXPIRED: + return false; + case LOCK_OWNER: + return lockService.getLockType(nodeRef) != LockType.WRITE_LOCK; + default: + return true; + } } } diff --git a/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQuery.java b/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQuery.java index 2b84bfce40..30e157cf94 100644 --- a/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQuery.java +++ b/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQuery.java @@ -18,8 +18,12 @@ */ package org.alfresco.repo.model.filefolder; +import java.io.Serializable; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQueryParameters; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.node.NodePropertyHelper; @@ -30,7 +34,9 @@ import org.alfresco.repo.node.getchildren.FilterProp; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; import org.alfresco.util.FileFilterMode; import org.alfresco.util.FileFilterMode.Client; @@ -43,13 +49,14 @@ import org.alfresco.util.FileFilterMode.Client; * This is the same as the nodes getchildren canned query, except it takes into account hidden files and folders. * * @since 4.1.1 - * @author steveglover + * @author steveglover, janv * */ public class GetChildrenCannedQuery extends org.alfresco.repo.node.getchildren.GetChildrenCannedQuery { private HiddenAspect hiddenAspect; - + private DictionaryService dictionaryService; + public GetChildrenCannedQuery( NodeDAO nodeDAO, QNameDAO qnameDAO, @@ -58,12 +65,15 @@ public class GetChildrenCannedQuery extends org.alfresco.repo.node.getchildren.G TenantService tenantService, MethodSecurityBean methodSecurity, CannedQueryParameters params, - HiddenAspect hiddenAspect) + HiddenAspect hiddenAspect, + DictionaryService dictionaryService) { - super(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, methodSecurity, params); - this.hiddenAspect = hiddenAspect; + super(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, methodSecurity, params); + + this.hiddenAspect = hiddenAspect; + this.dictionaryService = dictionaryService; } - + @Override protected UnsortedChildQueryCallback getUnsortedChildQueryCallback(final List rawResult, final int requestedCount) { @@ -80,29 +90,54 @@ public class GetChildrenCannedQuery extends org.alfresco.repo.node.getchildren.G private class FileFolderFilterSortChildQueryCallback extends DefaultFilterSortChildQueryCallback { - public FileFolderFilterSortChildQueryCallback(List children, List filterProps) - { - super(children, filterProps); - } - - @Override - protected boolean include(FilterSortNode node) - { - boolean ret = super.include(node); - + private Map isTypeFolderMap = new HashMap(10); + + public FileFolderFilterSortChildQueryCallback(List children, List filterProps) + { + super(children, filterProps); + } + + @Override + protected boolean include(FilterSortNode node) + { + boolean ret = super.include(node); + // only visible files are returned, relative to the client type. - try - { - final Client client = FileFilterMode.getClient(); - return ret && hiddenAspect.getVisibility(client, node.getNodeRef()) != Visibility.NotVisible; + try + { + final Client client = FileFilterMode.getClient(); + return ret && hiddenAspect.getVisibility(client, node.getNodeRef()) != Visibility.NotVisible; } catch(AccessDeniedException e) { - // user may not have permission to determine the visibility of the node - return ret; + // user may not have permission to determine the visibility of the node + return ret; } - } - + } + + @Override + public boolean handle(FilterSortNode node) + { + super.handle(node); + + Map propVals = node.getPropVals(); + QName nodeTypeQName = (QName)propVals.get(GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE); + + if (nodeTypeQName != null) + { + // ALF-13968 + Boolean isFolder = isTypeFolderMap.get(nodeTypeQName); + if (isFolder == null) + { + isFolder = dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER); + isTypeFolderMap.put(nodeTypeQName, isFolder); + } + + propVals.put(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, isFolder); + } + + return true; + } } private class FileFolderUnsortedChildQueryCallback extends DefaultUnsortedChildQueryCallback diff --git a/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQueryFactory.java b/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQueryFactory.java index b72a33e3a5..5ac6871c27 100644 --- a/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/model/filefolder/GetChildrenCannedQueryFactory.java @@ -44,6 +44,6 @@ public class GetChildrenCannedQueryFactory extends org.alfresco.repo.node.getchi { NodePropertyHelper nodePropertyHelper = new NodePropertyHelper(dictionaryService, qnameDAO, localeDAO, contentDataDAO); - return (CannedQuery) new GetChildrenCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, methodSecurity, parameters, hiddenAspect); + return (CannedQuery) new GetChildrenCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, nodePropertyHelper, tenantService, methodSecurity, parameters, hiddenAspect, dictionaryService); } } diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java index 3ea7ab1930..295042123a 100644 --- a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -85,6 +85,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions filterSortProps, FilterSortNodeEntity params) { int cnt = 0; + int propCnt = 0; for (QName filterSortProp : filterSortProps) { @@ -292,7 +294,7 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions permHits = new HashSet(100); private Set permMisses = new HashSet(100); - private NodeRef testFolder; - @SuppressWarnings({ "rawtypes" }) private NamedObjectRegistry cannedQueryRegistry; + private static final String CQ_FACTORY_NAME = "fileFolderGetChildrenCannedQueryFactory"; @SuppressWarnings({ "unchecked", "rawtypes" }) @Override @@ -131,12 +141,13 @@ public class GetChildrenCannedQueryTest extends TestCase nodeService = (NodeService)ctx.getBean("NodeService"); contentService = (ContentService)ctx.getBean("ContentService"); mimetypeService = (MimetypeService)ctx.getBean("MimetypeService"); + dictionaryService = (DictionaryService)ctx.getBean("DictionaryService"); personService = (PersonService)ctx.getBean("PersonService"); authenticationService = (MutableAuthenticationService)ctx.getBean("AuthenticationService"); permissionService = (PermissionService)ctx.getBean("PermissionService"); ratingService = (RatingService)ctx.getBean("RatingService"); - + dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO"); tenantService = (TenantService) ctx.getBean("tenantService"); @@ -145,7 +156,7 @@ public class GetChildrenCannedQueryTest extends TestCase GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = new GetChildrenCannedQueryFactory(); - getChildrenCannedQueryFactory.setBeanName("getChildrenCannedQueryFactory"); + getChildrenCannedQueryFactory.setBeanName("fileFolderGetChildrenCannedQueryFactory"); getChildrenCannedQueryFactory.setRegistry(cannedQueryRegistry); getChildrenCannedQueryFactory.setCannedQueryDAO((CannedQueryDAO)ctx.getBean("cannedQueryDAO")); @@ -155,16 +166,17 @@ public class GetChildrenCannedQueryTest extends TestCase getChildrenCannedQueryFactory.setLocaleDAO((LocaleDAO)ctx.getBean("localeDAO")); getChildrenCannedQueryFactory.setNodeDAO((NodeDAO)ctx.getBean("nodeDAO")); getChildrenCannedQueryFactory.setQnameDAO((QNameDAO)ctx.getBean("qnameDAO")); + getChildrenCannedQueryFactory.setHiddenAspect((HiddenAspect)ctx.getBean("hiddenAspect")); getChildrenCannedQueryFactory.setMethodSecurity((MethodSecurityBean)ctx.getBean("FileFolderService_security_list")); getChildrenCannedQueryFactory.afterPropertiesSet(); - + fiveStarRatingScheme = ratingService.getRatingScheme("fiveStarRatingScheme"); assertNotNull(fiveStarRatingScheme); likesRatingScheme = ratingService.getRatingScheme("likesRatingScheme"); assertNotNull(likesRatingScheme); - + if (! setupTestData) { AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); @@ -179,7 +191,7 @@ public class GetChildrenCannedQueryTest extends TestCase bootstrap.setDictionaryDAO(dictionaryDAO); bootstrap.setTenantService(tenantService); bootstrap.bootstrap(); - + createUser(TEST_USER_PREFIX, TEST_USER, TEST_USER); createUser(TEST_USER_PREFIX+"aaaa", TEST_USER_PREFIX+"bbbb", TEST_USER_PREFIX+"cccc"); @@ -187,13 +199,16 @@ public class GetChildrenCannedQueryTest extends TestCase createUser(TEST_USER_PREFIX+"dddd", TEST_USER_PREFIX+"ffff", TEST_USER_PREFIX+"gggg"); createUser(TEST_USER_PREFIX+"hhhh", TEST_USER_PREFIX+"cccc", TEST_USER_PREFIX+"jjjj"); - NodeRef testParentFolder = repositoryHelper.getCompanyHome(); + NodeRef testParentFolder = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); - // create folder subtype - createFolder(testParentFolder, "emptySystemFolder", ContentModel.TYPE_SYSTEM_FOLDER); + // create folder subtype (note: system folder here) + createFolder(testParentFolder, FOLDER_3, TEST_FOLDER_SUBTYPE); // create content subtype (note: no pun intended ... "cm:savedquery" already exists in content model ... but is NOT related to canned queries !) - createContent(testParentFolder, "textContent", QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "savedquery")); + createContent(testParentFolder, "textContent", TEST_CONTENT_SUBTYPE); + + createFolder(testParentFolder, FOLDER_5, ContentModel.TYPE_FOLDER); + createFolder(testParentFolder, FOLDER_4, ContentModel.TYPE_FOLDER); boolean canRead = true; @@ -210,8 +225,11 @@ public class GetChildrenCannedQueryTest extends TestCase loadContent(testParentFolder, "quick.gif", "YY title "+TEST_RUN, "BB description", canRead, permMisses); loadContent(testParentFolder, "quick.xml", "ZZ title" +TEST_RUN, "BB description", canRead, permMisses); + createFolder(testParentFolder, FOLDER_2, ContentModel.TYPE_FOLDER); + createFolder(testParentFolder, FOLDER_1, ContentModel.TYPE_FOLDER); + setupTestData = true; - + // double-check permissions - see testPermissions AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); @@ -241,18 +259,29 @@ public class GetChildrenCannedQueryTest extends TestCase assertTrue(permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED); } } - + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - testFolder = createFolder(repositoryHelper.getCompanyHome(), "testFolder1", QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "folder")); + + NodeRef testFolder = createFolder(repositoryHelper.getCompanyHome(), "GetChildrenCannedQueryTest-testFolder-"+TEST_RUN_ID, QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "folder")); createContent(testFolder, "textContent1", ContentModel.TYPE_CONTENT); createContent(testFolder, QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "contains1"), "textContent2", ContentModel.TYPE_CONTENT); - + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); } + private NodeRef getOrCreateParentTestFolder(String name) throws Exception + { + NodeRef testFolder = nodeService.getChildByName(repositoryHelper.getCompanyHome(), ContentModel.ASSOC_CONTAINS, name); + if (testFolder == null) + { + testFolder = createFolder(repositoryHelper.getCompanyHome(), name, ContentModel.TYPE_FOLDER); + } + return testFolder; + } + public void testSetup() throws Exception { - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); PagingResults results = list(parentNodeRef, -1, -1, 0); assertTrue(results.getPage().size() > 3); @@ -260,7 +289,7 @@ public class GetChildrenCannedQueryTest extends TestCase public void testMaxItems() throws Exception { - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); PagingResults results = list(parentNodeRef, -1, -1, 0); assertFalse(results.hasMoreItems()); @@ -285,7 +314,7 @@ public class GetChildrenCannedQueryTest extends TestCase public void testPaging() throws Exception { - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); PagingResults results = list(parentNodeRef, -1, -1, 0); assertFalse(results.hasMoreItems()); @@ -339,7 +368,7 @@ public class GetChildrenCannedQueryTest extends TestCase public void testTypeFiltering() throws Exception { - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); // note: parent should contain test example(s) of each type @@ -395,7 +424,7 @@ public class GetChildrenCannedQueryTest extends TestCase public void testPropertyStringFiltering() throws Exception { - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); filterByPropAndCheck(parentNodeRef, ContentModel.PROP_NAME, "GC-CQ-File-"+TEST_RUN+"-", FilterTypeString.STARTSWITH, 5); filterByPropAndCheck(parentNodeRef, ContentModel.PROP_NAME, "gc-CQ-File-"+TEST_RUN+"-", FilterTypeString.STARTSWITH, 0); @@ -477,7 +506,8 @@ public class GetChildrenCannedQueryTest extends TestCase public void testPropertySorting() throws Exception { - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); + NodeRef nodeRef1 = null; NodeRef nodeRef2 = null; NodeRef nodeRef3 = null; @@ -494,17 +524,17 @@ public class GetChildrenCannedQueryTest extends TestCase AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); try { - nodeRef1 = createContent(parentNodeRef, "rating1", ContentModel.TYPE_CONTENT); - nodeRef2 = createContent(parentNodeRef, "rating2", ContentModel.TYPE_CONTENT); - nodeRef3 = createContent(parentNodeRef, "rating3", ContentModel.TYPE_CONTENT); - nodeRef4 = createContent(parentNodeRef, "rating4", ContentModel.TYPE_CONTENT); - nodeRef5 = createContent(parentNodeRef, "rating5", ContentModel.TYPE_CONTENT); - - nodeRef6 = createContent(parentNodeRef, "rating6", ContentModel.TYPE_CONTENT); - nodeRef7 = createContent(parentNodeRef, "rating7", ContentModel.TYPE_CONTENT); - nodeRef8 = createContent(parentNodeRef, "rating8", ContentModel.TYPE_CONTENT); - nodeRef9 = createContent(parentNodeRef, "rating9", ContentModel.TYPE_CONTENT); - nodeRef10 = createContent(parentNodeRef, "rating10", ContentModel.TYPE_CONTENT); + nodeRef1 = createContent(parentNodeRef, "rating1", ContentModel.TYPE_CONTENT); + nodeRef2 = createContent(parentNodeRef, "rating2", ContentModel.TYPE_CONTENT); + nodeRef3 = createContent(parentNodeRef, "rating3", ContentModel.TYPE_CONTENT); + nodeRef4 = createContent(parentNodeRef, "rating4", ContentModel.TYPE_CONTENT); + nodeRef5 = createContent(parentNodeRef, "rating5", ContentModel.TYPE_CONTENT); + + nodeRef6 = createContent(parentNodeRef, "rating6", ContentModel.TYPE_CONTENT); + nodeRef7 = createContent(parentNodeRef, "rating7", ContentModel.TYPE_CONTENT); + nodeRef8 = createContent(parentNodeRef, "rating8", ContentModel.TYPE_CONTENT); + nodeRef9 = createContent(parentNodeRef, "rating9", ContentModel.TYPE_CONTENT); + nodeRef10 = createContent(parentNodeRef, "rating10", ContentModel.TYPE_CONTENT); } finally { @@ -523,7 +553,7 @@ public class GetChildrenCannedQueryTest extends TestCase ratingService.applyRating(nodeRef8, 1.0f, "likesRatingScheme"); ratingService.applyRating(nodeRef9, 1.0f, "likesRatingScheme"); ratingService.applyRating(nodeRef10, 1.0f, "likesRatingScheme"); - + // do children canned query PagingResults results = list(parentNodeRef, -1, -1, 0); @@ -538,10 +568,13 @@ public class GetChildrenCannedQueryTest extends TestCase sortQNames.add(ContentModel.PROP_CREATOR); sortQNames.add(ContentModel.PROP_MODIFIED); sortQNames.add(ContentModel.PROP_MODIFIER); + + // special (pseudo) content/node props sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_CONTENT_SIZE); sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_CONTENT_MIMETYPE); sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE); - + sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER); + // Add in the ratings properties on which to sort sortQNames.add(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "fiveStarRatingSchemeCount")); sortQNames.add(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "likesRatingSchemeCount")); @@ -553,7 +586,7 @@ public class GetChildrenCannedQueryTest extends TestCase sortAndCheck(parentNodeRef, sortQName, true); // ascending } - // sort with two props + // sort with two props - title first, then description List> sortPairs = new ArrayList>(3); sortPairs.add(new Pair(ContentModel.PROP_TITLE, false)); sortPairs.add(new Pair(ContentModel.PROP_DESCRIPTION, false)); @@ -562,12 +595,40 @@ public class GetChildrenCannedQueryTest extends TestCase assertEquals(TEST_FILE_PREFIX+"quick.pdf", nodeService.getProperty(results.getPage().get(0), ContentModel.PROP_NAME)); // ZZ title + YY description assertEquals(TEST_FILE_PREFIX+"quick.txt", nodeService.getProperty(results.getPage().get(1), ContentModel.PROP_NAME)); // ZZ title + XX description + // sort with two props - folders first (ALF-13968) then by name + sortPairs = new ArrayList>(3); + sortPairs.add(new Pair(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, false)); + sortPairs.add(new Pair(ContentModel.PROP_NAME, true)); + + results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs); + + assertEquals(FOLDER_1, nodeService.getProperty(results.getPage().get(0), ContentModel.PROP_NAME)); + assertEquals(FOLDER_2, nodeService.getProperty(results.getPage().get(1), ContentModel.PROP_NAME)); + assertEquals(FOLDER_3, nodeService.getProperty(results.getPage().get(2), ContentModel.PROP_NAME)); + assertEquals(FOLDER_4, nodeService.getProperty(results.getPage().get(3), ContentModel.PROP_NAME)); + assertEquals(FOLDER_5, nodeService.getProperty(results.getPage().get(4), ContentModel.PROP_NAME)); + + assertEquals(TEST_FILE_PREFIX+"quick.bmp", nodeService.getProperty(results.getPage().get(5), ContentModel.PROP_NAME)); + assertEquals(TEST_FILE_PREFIX+"quick.doc", nodeService.getProperty(results.getPage().get(6), ContentModel.PROP_NAME)); sortPairs = new ArrayList>(3); - sortPairs.add(new Pair(ContentModel.PROP_NAME, true)); - sortPairs.add(new Pair(ContentModel.PROP_TITLE, true)); - sortPairs.add(new Pair(ContentModel.PROP_DESCRIPTION, true)); - sortPairs.add(new Pair(ContentModel.PROP_MODIFIED, true)); + sortPairs.add(new Pair(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, true)); + sortPairs.add(new Pair(ContentModel.PROP_NAME, false)); + + results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs); + + // note: this test assumes that children fit on one page + int len = results.getPage().size(); + + assertEquals("textContent", nodeService.getProperty(results.getPage().get(0), ContentModel.PROP_NAME)); + assertEquals("rating9", nodeService.getProperty(results.getPage().get(1), ContentModel.PROP_NAME)); + + assertEquals(FOLDER_5, nodeService.getProperty(results.getPage().get(len-5), ContentModel.PROP_NAME)); + assertEquals(FOLDER_4, nodeService.getProperty(results.getPage().get(len-4), ContentModel.PROP_NAME)); + assertEquals(FOLDER_3, nodeService.getProperty(results.getPage().get(len-3), ContentModel.PROP_NAME)); + assertEquals(FOLDER_2, nodeService.getProperty(results.getPage().get(len-2), ContentModel.PROP_NAME)); + assertEquals(FOLDER_1, nodeService.getProperty(results.getPage().get(len-1), ContentModel.PROP_NAME)); + // TODO - sort with three props @@ -575,6 +636,12 @@ public class GetChildrenCannedQueryTest extends TestCase try { // -ve test + sortPairs = new ArrayList>(3); + sortPairs.add(new Pair(ContentModel.PROP_NAME, true)); + sortPairs.add(new Pair(ContentModel.PROP_TITLE, true)); + sortPairs.add(new Pair(ContentModel.PROP_DESCRIPTION, true)); + sortPairs.add(new Pair(ContentModel.PROP_MODIFIED, true)); + results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs); fail("Unexpected - cannot sort with more than three props"); } @@ -588,7 +655,7 @@ public class GetChildrenCannedQueryTest extends TestCase { AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); - NodeRef parentNodeRef = repositoryHelper.getCompanyHome(); + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-"+TEST_RUN_ID); PagingResults results = list(parentNodeRef, -1, -1, 0); assertFalse(results.hasMoreItems()); @@ -627,7 +694,7 @@ public class GetChildrenCannedQueryTest extends TestCase { AuthenticationUtil.pushAuthentication(); AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - + NodeRef parentNodeRef = nodeService.createNode( repositoryHelper.getCompanyHome(), ContentModel.ASSOC_CONTAINS, @@ -714,8 +781,10 @@ public class GetChildrenCannedQueryTest extends TestCase assertEquals("", 0, totalCnt); } - public void testRestrictByAssocType() + public void testRestrictByAssocType() throws Exception { + NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-testFolder-"+TEST_RUN_ID); + Set assocTypeQNames = new HashSet(3); Set childTypeQNames = new HashSet(3); @@ -723,14 +792,14 @@ public class GetChildrenCannedQueryTest extends TestCase assocTypeQNames.add(ContentModel.ASSOC_CONTAINS); childTypeQNames.clear(); childTypeQNames.add(ContentModel.TYPE_CONTENT); - List children = filterByAssocTypeAndCheck(testFolder, assocTypeQNames, childTypeQNames); + List children = filterByAssocTypeAndCheck(parentNodeRef, assocTypeQNames, childTypeQNames); assertEquals(1, children.size()); assocTypeQNames.clear(); assocTypeQNames.add(QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "contains1")); childTypeQNames.clear(); childTypeQNames.add(ContentModel.TYPE_CONTENT); - children = filterByAssocTypeAndCheck(testFolder, assocTypeQNames, childTypeQNames); + children = filterByAssocTypeAndCheck(parentNodeRef, assocTypeQNames, childTypeQNames); assertEquals(1, children.size()); assocTypeQNames.clear(); @@ -738,7 +807,7 @@ public class GetChildrenCannedQueryTest extends TestCase assocTypeQNames.add(ContentModel.ASSOC_CONTAINS); childTypeQNames.clear(); childTypeQNames.add(ContentModel.TYPE_CONTENT); - children = filterByAssocTypeAndCheck(testFolder, assocTypeQNames, childTypeQNames); + children = filterByAssocTypeAndCheck(parentNodeRef, assocTypeQNames, childTypeQNames); assertEquals(2, children.size()); } @@ -749,7 +818,7 @@ public class GetChildrenCannedQueryTest extends TestCase pagingRequest.setRequestTotalCountMax(requestTotalCountMax); // get canned query - GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject("getChildrenCannedQueryFactory"); + GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CQ_FACTORY_NAME); GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(parentNodeRef, pattern, null, null, null, sortProps, pagingRequest); // execute canned query @@ -925,6 +994,10 @@ public class GetChildrenCannedQueryTest extends TestCase { val = nodeService.getType(nodeRef); } + else if (sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER)) + { + val = dictionaryService.isSubClass(nodeService.getType(nodeRef), ContentModel.TYPE_FOLDER); + } else { val = nodeService.getProperty(nodeRef, sortPropQName); @@ -972,27 +1045,34 @@ public class GetChildrenCannedQueryTest extends TestCase { result = ((QName)val).compareTo((QName)prevVal); } + else if (val instanceof Boolean) + { + result = ((Boolean)val).compareTo((Boolean)prevVal); + } else { fail("Unsupported sort type ("+nodeRef+"): "+val.getClass().getName()); } + String prevName = (String)nodeService.getProperty(prevNodeRef, ContentModel.PROP_NAME); + String currName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + if (! sortAscending) { assertTrue( - "Not descending: \n" + + "Not descending: "+result+"\n" + " Iteration: " + currentIteration + " out of " + count + "\n" + - " Previous: " + prevNodeRef + " had " + prevVal + "\n" + - " Current : " + nodeRef + " had " + val, + " Previous: " + prevNodeRef + " had " + prevVal + " ("+prevName+")\n" + + " Current : " + nodeRef + " had " + val + " ("+currName+")", result <= 0); } else { assertTrue( - "Not ascending: \n" + + "Not ascending: "+result+"\n" + " Iteration: " + currentIteration + " out of " + count + "\n" + - " Previous: " + prevNodeRef + " had " + prevVal + "\n" + - " Current : " + nodeRef + " had " + val, + " Previous: " + prevNodeRef + " had " + prevVal + " ("+prevName+")\n" + + " Current : " + nodeRef + " had " + val + " ("+currName+")", result >= 0); } } @@ -1015,8 +1095,8 @@ public class GetChildrenCannedQueryTest extends TestCase PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems, null); pagingRequest.setRequestTotalCountMax(requestTotalCountMax); - // get canned query - GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject("getChildrenCannedQueryFactory"); + // get canned query (note: test the fileFolder extension - including support for sorting folders first) + GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CQ_FACTORY_NAME); GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(parentNodeRef, null, null, childTypeQNames, filterProps, sortProps, pagingRequest); // execute canned query @@ -1040,7 +1120,7 @@ public class GetChildrenCannedQueryTest extends TestCase pagingRequest.setRequestTotalCountMax(requestTotalCountMax); // get canned query - GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject("getChildrenCannedQueryFactory"); + GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CQ_FACTORY_NAME); GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(parentNodeRef, null, assocTypeQNames, childTypeQNames, filterProps, sortProps, pagingRequest); // execute canned query diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index e554bfcfcd..e31c09b0d0 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -1205,8 +1205,8 @@ public class RuleServiceImpl if (subAction.getActionDefinitionName().equals(MailActionExecuter.NAME)) { subAction.setParameterValue(MailActionExecuter.PARAM_SEND_AFTER_COMMIT, true); - } - } + } + } } else if (action.getActionDefinitionName().equals(MailActionExecuter.NAME)) { diff --git a/source/java/org/alfresco/repo/rule/RuleTypeImpl.java b/source/java/org/alfresco/repo/rule/RuleTypeImpl.java index b8703aae04..59a812006e 100644 --- a/source/java/org/alfresco/repo/rule/RuleTypeImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleTypeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -127,22 +127,34 @@ public class RuleTypeImpl extends CommonResourceAbstractBase implements RuleType true, this.name); + String ruleContext = null; + if (logger.isDebugEnabled() == true) + { + if (actionedUponNodeRef != null) + { + ruleContext = (executeRuleImmediately ? " now" : " ") + " on " + nodeService.getPath(actionedUponNodeRef).toString().replaceAll("\\{[^}]*}", ""); + } + } + if (rules.size() != 0) { for (Rule rule : rules) - { + { if (logger.isDebugEnabled() == true) { - NodeRef ruleNodeRef = rule.getNodeRef(); if (nodeRef != null) { - logger.debug("Triggering rule " + ruleNodeRef.toString()); + ruleContext = " " + rule.getTitle() + ruleContext; } } // Only queue if the rule is not disabled - if (rule.getRuleDisabled() == false) + if (rule.getRuleDisabled() == false && ruleService.rulesEnabled(ruleService.getOwningNodeRef(rule))) { + if (logger.isDebugEnabled() == true) + { + logger.debug("Triggering rule" + ruleContext); + } if (executeRuleImmediately == false) { // Queue the rule to be executed at the end of the transaction (but still in the transaction) @@ -154,13 +166,17 @@ public class RuleTypeImpl extends CommonResourceAbstractBase implements RuleType ((RuntimeRuleService)ruleService).executeRule(rule, actionedUponNodeRef, null); } } + else if (logger.isDebugEnabled() == true) + { + logger.debug("Disabled rule" + ruleContext); + } } } else { if (logger.isDebugEnabled() == true) { - logger.debug("This node has no rules to trigger."); + logger.debug("No rules to trigger" + ruleContext); } } } diff --git a/source/java/org/alfresco/repo/rule/RulesAspect.java b/source/java/org/alfresco/repo/rule/RulesAspect.java index 838f8e0391..d8265ffa9f 100644 --- a/source/java/org/alfresco/repo/rule/RulesAspect.java +++ b/source/java/org/alfresco/repo/rule/RulesAspect.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.rule; +import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; @@ -47,7 +48,8 @@ public class RulesAspect implements CopyServicePolicies.OnCopyNodePolicy, CopyServicePolicies.OnCopyCompletePolicy, NodeServicePolicies.OnAddAspectPolicy, - NodeServicePolicies.BeforeDeleteChildAssociationPolicy + NodeServicePolicies.BeforeRemoveAspectPolicy, + NodeServicePolicies.BeforeDeleteNodePolicy { private PolicyComponent policyComponent; private BehaviourFilter behaviourFilter; @@ -95,11 +97,14 @@ public class RulesAspect implements NodeServicePolicies.OnAddAspectPolicy.QNAME, RuleModel.ASPECT_RULES, new JavaBehaviour(this, "onAddAspect")); - this.policyComponent.bindAssociationBehaviour( - NodeServicePolicies.BeforeDeleteChildAssociationPolicy.QNAME, + this.policyComponent.bindClassBehaviour( + NodeServicePolicies.BeforeRemoveAspectPolicy.QNAME, RuleModel.ASPECT_RULES, - RuleModel.ASSOC_RULE_FOLDER, - new JavaBehaviour(this, "beforeDeleteChildAssociation")); + new JavaBehaviour(this, "beforeRemoveAspect")); + this.policyComponent.bindClassBehaviour( + NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, + RuleModel.ASPECT_RULES, + new JavaBehaviour(this, "beforeDeleteNode")); } /** @@ -130,26 +135,65 @@ public class RulesAspect implements } } + /** - * ALF-11923 - * @since 4.1.1 - * @author Derek Hulley + * The rule folder & below will be deleted automatically in the normal way, so we don't need to worry about them. + * But we need additional handling for any other folders which have rules linked to this folder's rules. See + * ALF-11923, ALF-15262. + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeRemoveAspectPolicy#beforeRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @Override - public void beforeDeleteChildAssociation(ChildAssociationRef childAssocRef) + public void beforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) { - NodeRef nodeRef = childAssocRef.getParentRef(); + if (!aspectTypeQName.equals(RuleModel.ASPECT_RULES)) + { + return; + } + this.ruleService.disableRules(nodeRef); try { - // Just remove the aspect for the association - nodeService.removeAspect(nodeRef, RuleModel.ASPECT_RULES); + for (ChildAssociationRef childAssocRef : nodeService.getChildAssocs(nodeRef, RuleModel.ASSOC_RULE_FOLDER, + RuleModel.ASSOC_RULE_FOLDER, false)) + { + // We are only interested in the deletion of primary associations to a rule folder, which usually + // happens when all rules in a folder are deleted and the ASPECT_RULES aspect is removed + if (!childAssocRef.isPrimary()) + { + continue; + } + NodeRef savedRuleFolderRef = childAssocRef.getChildRef(); + // Cascade the removal to all secondary (linked) parents + List linkedAssocs = nodeService.getParentAssocs(savedRuleFolderRef); + for (ChildAssociationRef linkAssoc : linkedAssocs) + { + if (!linkAssoc.isPrimary()) + { + // Remove the aspect from linked parents; this will also delete the linking secondary + // association + nodeService.removeAspect(linkAssoc.getParentRef(), RuleModel.ASPECT_RULES); + } + } + } } finally { - ruleService.enableRules(nodeRef); + this.ruleService.enableRules(nodeRef); } } + + /** + * @since 3.4.11 + * @author Neil McErlean + */ + @Override + public void beforeDeleteNode(NodeRef nodeRef) + { + // In case the event isn't triggered automatically by node service (e.g. on a cascaded node tree deletion), + // trigger handling removal of the rules aspect. + beforeRemoveAspect(nodeRef, RuleModel.ASPECT_RULES); + } /** * @return Returns {@link RulesAspectCopyBehaviourCallback} diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java b/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java index 89a3e2ce3e..371a393a38 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java @@ -23,13 +23,11 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.ContentServicePolicies; -import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -38,7 +36,7 @@ import org.apache.commons.logging.LogFactory; * @author Roy Wetherall */ public class OnContentUpdateRuleTrigger extends RuleTriggerAbstractBase - implements ContentServicePolicies.OnContentUpdatePolicy + implements ContentServicePolicies.OnContentPropertyUpdatePolicy { /** * The logger @@ -78,53 +76,65 @@ public class OnContentUpdateRuleTrigger extends RuleTriggerAbstractBase { // Bind behaviour this.policyComponent.bindClassBehaviour( - ContentServicePolicies.OnContentUpdatePolicy.QNAME, + ContentServicePolicies.OnContentPropertyUpdatePolicy.QNAME, this, - new JavaBehaviour(this, "onContentUpdate")); + new JavaBehaviour(this, "onContentPropertyUpdate")); } + + /** - * @see org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy#onContentUpdate(org.alfresco.service.cmr.repository.NodeRef, boolean) + * @see org.alfresco.repo.content.ContentServicePolicies.OnContentPropertyUpdatePolicy#onContentPropertyUpdate(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.ContentData, org.alfresco.service.cmr.repository.ContentData) */ - public void onContentUpdate(NodeRef nodeRef, boolean newContent) + @Override + public void onContentPropertyUpdate(NodeRef nodeRef, QName propertyQName, ContentData beforeValue, + ContentData afterValue) { // Break out early if rules are not enabled if (!areRulesEnabled()) { return; } - - // Check the new content and make sure that we do indeed want to trigger the rule - boolean fail = false; - if (newContent == true) + + // Check the new content and make sure that we do indeed want to trigger the rule + if (propertyQName.equals(ContentModel.PROP_PREFERENCE_VALUES)) { - fail = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT); - - if (fail == false) - { - // Note: Don't use the ContentService.getReader() because we don't need access to the content - ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); - if (contentData == null) - { - fail = true; - } - } + return; } - - // Double check for content created in this transaction - if (fail == false && !newContent) + + // Check for new content + boolean newContent = beforeValue == null && afterValue != null; + + // Check the new content and make sure that we do indeed want to trigger the rule + if (newContent) + { + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT)) + { + return; + } + + // Note: Don't use the ContentService.getReader() because we don't need access to the content + if (!ContentData.hasContent(afterValue)) + { + return; + } + } + // An update, but double check for content created in this transaction + else { Set newNodeRefSet = TransactionalResourceHelper.getSet(RULE_TRIGGER_NEW_NODES); - boolean wasCreatedInTxn = newNodeRefSet.contains(nodeRef); - if (logger.isDebugEnabled() && wasCreatedInTxn) + if (newNodeRefSet.contains(nodeRef)) { - logger.debug("Receiving content property update for node created in transaction: " + nodeRef); + if (logger.isDebugEnabled()) + { + logger.debug("Receiving content property update for node created in transaction: " + nodeRef); + } + return; } - fail = wasCreatedInTxn; } // Trigger the rules in the appropriate way - if (fail == false && newContent == this.onNewContent) + if (newContent == this.onNewContent) { if (triggerParentRules == true) { diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/OnMoveNodeRuleTrigger.java b/source/java/org/alfresco/repo/rule/ruletrigger/OnMoveNodeRuleTrigger.java index 3d0db954de..197d70bff1 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/OnMoveNodeRuleTrigger.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/OnMoveNodeRuleTrigger.java @@ -1,8 +1,27 @@ +/* + * Copyright (C) 2005-2012 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.rule.ruletrigger; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -47,21 +66,32 @@ public class OnMoveNodeRuleTrigger extends RuleTriggerAbstractBase implements No // Check that it is not rename operation. if (!oldChildAssocRef.getParentRef().equals(newChildAssocRef.getParentRef())) { - triggerChildrenRules(newChildAssocRef, newChildAssocRef); + triggerChildrenRules(newChildAssocRef); } } - private void triggerChildrenRules(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) + private void triggerChildrenRules(ChildAssociationRef newChildAssocRef) { - // Break out early if rules are not enabled - if (!areRulesEnabled()) + NodeRef nodeRef = newChildAssocRef.getChildRef(); + boolean enabled = ruleService.rulesEnabled(nodeRef); + try { - return; + if (enabled) + { + ruleService.disableRules(nodeRef); + } + triggerRules(newChildAssocRef.getParentRef(), nodeRef); + for (ChildAssociationRef ref : nodeService.getChildAssocs(nodeRef)) + { + triggerChildrenRules(ref); + } } - triggerRules(newChildAssocRef.getParentRef(), newChildAssocRef.getChildRef()); - for (ChildAssociationRef ref : nodeService.getChildAssocs(newChildAssocRef.getChildRef())) + finally { - triggerChildrenRules(ref, ref); + if (enabled) + { + ruleService.enableRules(nodeRef); + } } } } diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java index cf3a995025..67f1f77346 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -37,194 +37,195 @@ import org.alfresco.util.BaseSpringTest; */ public class RuleTriggerTest extends BaseSpringTest { - private static final String ON_CREATE_NODE_TRIGGER = "on-create-node-trigger"; - private static final String ON_UPDATE_NODE_TRIGGER = "on-update-node-trigger"; - private static final String ON_CREATE_CHILD_ASSOCIATION_TRIGGER = "on-create-child-association-trigger"; - private static final String ON_DELETE_CHILD_ASSOCIATION_TRIGGER = "on-delete-child-association-trigger"; - private static final String ON_CREATE_ASSOCIATION_TRIGGER = "on-create-association-trigger"; - private static final String ON_DELETE_ASSOCIATION_TRIGGER = "on-delete-association-trigger"; - private static final String ON_CONTENT_UPDATE_TRIGGER = "on-content-update-trigger"; + private static final String ON_CREATE_NODE_TRIGGER = "on-create-node-trigger"; + private static final String ON_UPDATE_NODE_TRIGGER = "on-update-node-trigger"; + private static final String ON_MOVE_NODE_TRIGGER = "on-move-node-trigger"; + private static final String ON_CREATE_CHILD_ASSOCIATION_TRIGGER = "on-create-child-association-trigger"; + private static final String ON_DELETE_CHILD_ASSOCIATION_TRIGGER = "on-delete-child-association-trigger"; + private static final String ON_CREATE_ASSOCIATION_TRIGGER = "on-create-association-trigger"; + private static final String ON_DELETE_ASSOCIATION_TRIGGER = "on-delete-association-trigger"; + private static final String ON_CONTENT_UPDATE_TRIGGER = "on-content-update-trigger"; private static final String ON_CONTENT_CREATE_TRIGGER = "on-content-create-trigger"; - private NodeService nodeService; - private ContentService contentService; - - private StoreRef testStoreRef; - private NodeRef rootNodeRef; - - @Override - protected void onSetUpInTransaction() throws Exception - { - ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); - this.nodeService = serviceRegistry.getNodeService(); - this.contentService = serviceRegistry.getContentService(); + private NodeService nodeService; + private ContentService contentService; + + private StoreRef testStoreRef; + private NodeRef rootNodeRef; + + @Override + protected void onSetUpInTransaction() throws Exception + { + ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); + this.nodeService = serviceRegistry.getNodeService(); + this.contentService = serviceRegistry.getContentService(); AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); - - this.testStoreRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + + this.testStoreRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); this.rootNodeRef = this.nodeService.getRootNode(this.testStoreRef); - } - - @Override + } + + @Override protected void onTearDownInTransaction() throws Exception { AuthenticationUtil.clearCurrentSecurityContext(); } public void testOnCreateNodeTrigger() - { - TestRuleType ruleType = createTestRuleType(ON_CREATE_NODE_TRIGGER); - assertFalse(ruleType.rulesTriggered); - - // Try and trigger the type + { + TestRuleType ruleType = createTestRuleType(ON_CREATE_NODE_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER); - - // Check to see if the rule type has been triggered + + // Check to see if the rule type has been triggered assertTrue(ruleType.rulesTriggered); - } - - public void testOnUpdateNodeTrigger() - { - NodeRef nodeRef = this.nodeService.createNode( + } + + public void testOnUpdateNodeTrigger() + { + NodeRef nodeRef = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - - TestRuleType ruleType = createTestRuleType(ON_UPDATE_NODE_TRIGGER); - assertFalse(ruleType.rulesTriggered); - - // Try and trigger the type - this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, "nameChanged"); - - // Check to see if the rule type has been triggered - assertTrue(ruleType.rulesTriggered); - } - -// public void testOnDeleteNodeTrigger() -// { -// NodeRef nodeRef = this.nodeService.createNode( + + TestRuleType ruleType = createTestRuleType(ON_UPDATE_NODE_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type + this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, "nameChanged"); + + // Check to see if the rule type has been triggered + assertTrue(ruleType.rulesTriggered); + } + +// public void testOnDeleteNodeTrigger() +// { +// NodeRef nodeRef = this.nodeService.createNode( // this.rootNodeRef, -// ContentModel.ASSOC_CHILDREN, +// ContentModel.ASSOC_CHILDREN, // ContentModel.ASSOC_CHILDREN, // ContentModel.TYPE_CONTAINER).getChildRef(); -// -// TestRuleType ruleType = createTestRuleType(ON_DELETE_NODE_TRIGGER); -// assertFalse(ruleType.rulesTriggered); -// -// // Try and trigger the type -// this.nodeService.deleteNode(nodeRef); -// -// // Check to see if the rule type has been triggered -// assertTrue(ruleType.rulesTriggered); -// } - - public void testOnCreateChildAssociationTrigger() - { - NodeRef nodeRef = this.nodeService.createNode( +// +// TestRuleType ruleType = createTestRuleType(ON_DELETE_NODE_TRIGGER); +// assertFalse(ruleType.rulesTriggered); +// +// // Try and trigger the type +// this.nodeService.deleteNode(nodeRef); +// +// // Check to see if the rule type has been triggered +// assertTrue(ruleType.rulesTriggered); +// } + + public void testOnCreateChildAssociationTrigger() + { + NodeRef nodeRef = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - NodeRef nodeRef2 = this.nodeService.createNode( + NodeRef nodeRef2 = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - - TestRuleType ruleType = createTestRuleType(ON_CREATE_CHILD_ASSOCIATION_TRIGGER); - assertFalse(ruleType.rulesTriggered); - - // Try and trigger the type - this.nodeService.addChild( - nodeRef, - nodeRef2, - ContentModel.ASSOC_CHILDREN, + + TestRuleType ruleType = createTestRuleType(ON_CREATE_CHILD_ASSOCIATION_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type + this.nodeService.addChild( + nodeRef, + nodeRef2, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN); - - // Check to see if the rule type has been triggered - assertTrue(ruleType.rulesTriggered); - } - - public void testOnDeleteChildAssociationTrigger() - { - NodeRef nodeRef = this.nodeService.createNode( + + // Check to see if the rule type has been triggered + assertTrue(ruleType.rulesTriggered); + } + + public void testOnDeleteChildAssociationTrigger() + { + NodeRef nodeRef = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - NodeRef nodeRef2 = this.nodeService.createNode( + NodeRef nodeRef2 = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - this.nodeService.addChild( - nodeRef, - nodeRef2, - ContentModel.ASSOC_CHILDREN, + this.nodeService.addChild( + nodeRef, + nodeRef2, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN); - - TestRuleType ruleType = createTestRuleType(ON_DELETE_CHILD_ASSOCIATION_TRIGGER); - assertFalse(ruleType.rulesTriggered); - - // Try and trigger the type - this.nodeService.removeChild(nodeRef, nodeRef2); - - // Check to see if the rule type has been triggered - assertTrue(ruleType.rulesTriggered); - } - - public void testOnCreateAssociationTrigger() - { - NodeRef nodeRef = this.nodeService.createNode( + + TestRuleType ruleType = createTestRuleType(ON_DELETE_CHILD_ASSOCIATION_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type + this.nodeService.removeChild(nodeRef, nodeRef2); + + // Check to see if the rule type has been triggered + assertTrue(ruleType.rulesTriggered); + } + + public void testOnCreateAssociationTrigger() + { + NodeRef nodeRef = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - NodeRef nodeRef2 = this.nodeService.createNode( + NodeRef nodeRef2 = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - - TestRuleType ruleType = createTestRuleType(ON_CREATE_ASSOCIATION_TRIGGER); - assertFalse(ruleType.rulesTriggered); - - // Try and trigger the type - this.nodeService.createAssociation(nodeRef, nodeRef2, ContentModel.ASSOC_CHILDREN); - - // Check to see if the rule type has been triggered - assertTrue(ruleType.rulesTriggered); - } - - public void testOnDeleteAssociationTrigger() - { - NodeRef nodeRef = this.nodeService.createNode( + + TestRuleType ruleType = createTestRuleType(ON_CREATE_ASSOCIATION_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type + this.nodeService.createAssociation(nodeRef, nodeRef2, ContentModel.ASSOC_CHILDREN); + + // Check to see if the rule type has been triggered + assertTrue(ruleType.rulesTriggered); + } + + public void testOnDeleteAssociationTrigger() + { + NodeRef nodeRef = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - NodeRef nodeRef2 = this.nodeService.createNode( + NodeRef nodeRef2 = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTAINER).getChildRef(); - this.nodeService.createAssociation(nodeRef, nodeRef2, ContentModel.ASSOC_CHILDREN); - - TestRuleType ruleType = createTestRuleType(ON_DELETE_ASSOCIATION_TRIGGER); - assertFalse(ruleType.rulesTriggered); - - // Try and trigger the type - this.nodeService.removeAssociation(nodeRef, nodeRef2, ContentModel.ASSOC_CHILDREN); - - // Check to see if the rule type has been triggered - assertTrue(ruleType.rulesTriggered); - } - + this.nodeService.createAssociation(nodeRef, nodeRef2, ContentModel.ASSOC_CHILDREN); + + TestRuleType ruleType = createTestRuleType(ON_DELETE_ASSOCIATION_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type + this.nodeService.removeAssociation(nodeRef, nodeRef2, ContentModel.ASSOC_CHILDREN); + + // Check to see if the rule type has been triggered + assertTrue(ruleType.rulesTriggered); + } + public void testOnContentCreateTrigger() { NodeRef nodeRef = this.nodeService.createNode( @@ -257,25 +258,25 @@ public class RuleTriggerTest extends BaseSpringTest assertFalse(contentCreate.rulesTriggered); } - public void testOnContentUpdateTrigger() - { - NodeRef nodeRef = this.nodeService.createNode( + public void testOnContentUpdateTrigger() + { + NodeRef nodeRef = this.nodeService.createNode( this.rootNodeRef, - ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTENT).getChildRef(); - + TestRuleType contentCreate = createTestRuleType(ON_CONTENT_CREATE_TRIGGER); - TestRuleType contentUpdate = createTestRuleType(ON_CONTENT_UPDATE_TRIGGER); + TestRuleType contentUpdate = createTestRuleType(ON_CONTENT_UPDATE_TRIGGER); assertFalse(contentCreate.rulesTriggered); assertFalse(contentUpdate.rulesTriggered); - - // Try and trigger the type - ContentWriter contentWriter = this.contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + + // Try and trigger the type + ContentWriter contentWriter = this.contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); contentWriter.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); contentWriter.setEncoding("UTF-8"); - contentWriter.putContent("some content"); - + contentWriter.putContent("some content"); + // Check to see if the rule type has been triggered assertTrue(contentCreate.rulesTriggered); assertFalse(contentUpdate.rulesTriggered); @@ -311,35 +312,67 @@ public class RuleTriggerTest extends BaseSpringTest assertTrue( "Content update must not fire if the content was created in the same txn.", contentUpdate.rulesTriggered); - } - - private TestRuleType createTestRuleType(String ruleTriggerName) - { - RuleTrigger ruleTrigger = (RuleTrigger)this.applicationContext.getBean(ruleTriggerName); - assertNotNull(ruleTrigger); - TestRuleType ruleType = new TestRuleType(); - ruleTrigger.registerRuleType(ruleType); - return ruleType; - } - - private class TestRuleType implements RuleType - { - public boolean rulesTriggered = false; + } + + public void testOnMoveNodeTrigger() + { + NodeRef nodeRef1 = this.nodeService.createNode(this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_CONTAINER).getChildRef(); - public String getName() - { - return "testRuleType"; - } + NodeRef nodeRef2 = this.nodeService.createNode(this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_CONTAINER).getChildRef(); - public String getDisplayLabel() - { - return "displayLabel"; - } + this.nodeService.createNode(nodeRef2, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_CONTAINER).getChildRef(); - public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef, boolean executeRuleImmediately) - { - // Indicate that the rules have been triggered - this.rulesTriggered = true; - } - } + this.nodeService.createNode(nodeRef2, + ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_CONTAINER).getChildRef(); + + + TestRuleType ruleType = createTestRuleType(ON_MOVE_NODE_TRIGGER); + assertFalse(ruleType.rulesTriggered); + + // Try and trigger the type + this.nodeService.moveNode(nodeRef2, nodeRef1, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN); + + // Check to see if the rule type has been triggered + assertTrue(ruleType.rulesTriggered); + assertEquals(3, ruleType.triggerCount); + } + + private TestRuleType createTestRuleType(String ruleTriggerName) + { + RuleTrigger ruleTrigger = (RuleTrigger)this.applicationContext.getBean(ruleTriggerName); + assertNotNull(ruleTrigger); + TestRuleType ruleType = new TestRuleType(); + ruleTrigger.registerRuleType(ruleType); + return ruleType; + } + + private class TestRuleType implements RuleType + { + public boolean rulesTriggered = false; + public int triggerCount = 0; + + public String getName() + { + return "testRuleType"; + } + + public String getDisplayLabel() + { + return "displayLabel"; + } + + public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef, boolean executeRuleImmediately) + { + // Indicate that the rules have been triggered + this.rulesTriggered = true; + triggerCount++; + } + } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java index 187488fe6c..4308495b4e 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java @@ -371,6 +371,13 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp { return false; } + + // Don't worry about the cascade reindex impact of nodes that aren't containers + if (!mayHaveChildren(childRef)) + { + return false; + } + // Otherwise, if the parent has less children than the child has parents, then cascade // reindex the parent int childCount = nodeService.getChildAssocs(parentRef, RegexQNamePattern.MATCH_ALL, @@ -603,6 +610,59 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp } } + protected boolean deleteLeafOnly(String nodeRef, IndexReader reader, boolean delete) throws LuceneIndexException + { + boolean found = false; + try + { + TermDocs td = reader.termDocs(new Term("LEAFID", nodeRef)); + while (td.next()) + { + found = true; + if (delete) + { + reader.deleteDocument(td.doc()); + } + else + { + break; + } + } + td.close(); + if (found) + { + return true; + } + // For backward compatibility, use old method of locating non-container docs + td = reader.termDocs(new Term("ID", nodeRef)); + while (td.next()) + { + int doc = td.doc(); + Document document = reader.document(doc); + // Exclude all containers except the root (which is also a node!) + Field path = document.getField("PATH"); + if (path == null || path.stringValue().length() == 0) + { + found = true; + if (delete) + { + reader.deleteDocument(doc); + } + else + { + break; + } + } + } + td.close(); + } + catch (IOException e) + { + throw new LuceneIndexException("Failed to delete container and below for " + nodeRef, e); + } + return found; + } + protected Set deleteImpl(String nodeRef, IndexReader deltaReader, IndexReader mainReader) throws LuceneIndexException, IOException { @@ -786,7 +846,9 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp } // Now regenerate the containers for this node if necessary - if (cascade || isCategory(getDictionaryService().getType(nodeService.getType(nodeRef)))) + if (cascade && mayHaveChildren(nodeRef) + && !getCachedChildren(childAssociationsSinceFlush, nodeRef, cascade).isEmpty() + || isCategory(getDictionaryService().getType(nodeService.getType(nodeRef)))) { generateContainersAndBelow(nodeService.getPaths(nodeRef, false), docs, cascade, pathsToRegenerate, childAssociationsSinceFlush); @@ -830,6 +892,7 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp Document xdoc = new Document(); xdoc.add(new Field("ID", stringNodeRef, Field.Store.YES, Field.Index.NO_NORMS, Field.TermVector.NO)); xdoc.add(new Field("TX", nodeStatus.getChangeTxnId(), Field.Store.YES, Field.Index.NO_NORMS, Field.TermVector.NO)); + xdoc.add(new Field("LEAFID", stringNodeRef, Field.Store.YES, Field.Index.NO_NORMS, Field.TermVector.NO)); boolean isAtomic = true; for (QName propertyName : properties.keySet()) { @@ -1049,13 +1112,11 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp break; case REINDEX: // Remove from delta if present - deleteLeafOnly(nodeRef, deltaReader, true); - - // Only mask out the node if it is present in the main index - if (deleteLeafOnly(nodeRef, mainReader, false)); - { + if (!deleteLeafOnly(nodeRef, deltaReader, true)) + { + // Only mask out the node if it is present in the main index deletions.add(nodeRef); - } + } if (!nodeActionMap.containsKey(nodeRef)) { nodeActionMap.put(nodeRef, Action.REINDEX); @@ -1990,13 +2051,14 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp } public int updateFullTextSearch(int size) throws LuceneIndexException - { + { + checkAbleToDoWork(IndexUpdateStatus.ASYNCHRONOUS); + if(getLuceneConfig().isContentIndexingEnabled() == false) { return 0; } - checkAbleToDoWork(IndexUpdateStatus.ASYNCHRONOUS); // if (!mainIndexExists()) // { // remainingCount = size; diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java index 61139ed7a4..f4d8c4ed8e 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java @@ -40,7 +40,6 @@ import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; @@ -316,40 +315,6 @@ public abstract class AbstractLuceneIndexerImpl extends AbstractLuceneBase im return found; } - protected boolean deleteLeafOnly(String nodeRef, IndexReader reader, boolean delete) throws LuceneIndexException - { - boolean found = false; - try - { - TermDocs td = reader.termDocs(new Term("ID", nodeRef)); - while (td.next()) - { - int doc = td.doc(); - Document document = reader.document(doc); - // Exclude all containers except the root (which is also a node!) - Field path = document.getField("PATH"); - if (path == null || path.stringValue().length() == 0) - { - found = true; - if (delete) - { - reader.deleteDocument(doc); - } - else - { - break; - } - } - } - td.close(); - } - catch (IOException e) - { - throw new LuceneIndexException("Failed to delete container and below for " + nodeRef, e); - } - return found; - } - /** the maximum transformation time to allow atomically, defaulting to 20ms */ protected long maxAtomicTransformationTime = 20; diff --git a/source/java/org/alfresco/repo/search/impl/lucene/FilterIndexReaderByStringId.java b/source/java/org/alfresco/repo/search/impl/lucene/FilterIndexReaderByStringId.java index 2eb791e2bc..3d21cf09eb 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/FilterIndexReaderByStringId.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/FilterIndexReaderByStringId.java @@ -120,20 +120,32 @@ public class FilterIndexReaderByStringId extends FilterIndexReader } else { - TermQuery query = new TermQuery(new Term("ID", stringRef)); - Hits hits = searcher.search(query); - if (hits.length() > 0) + boolean found = false; + TermDocs td = in.termDocs(new Term("LEAFID", stringRef)); + while (td.next()) { - for (int i = 0; i < hits.length(); i++) + deletedDocuments.set(td.doc()); + found = true; + } + td.close(); + // For backward compatibility, use old method of locating non-container docs + if (!found) + { + TermQuery query = new TermQuery(new Term("ID", stringRef)); + Hits hits = searcher.search(query); + if (hits.length() > 0) { - Document doc = hits.doc(i); - // Exclude all containers except the root (which is also a node!) - Field path = doc.getField("PATH"); - if (path == null || path.stringValue().length() == 0) + for (int i = 0; i < hits.length(); i++) { - deletedDocuments.set(hits.id(i)); - // There should only be one thing to delete - // break; + Document doc = hits.doc(i); + // Exclude all containers except the root (which is also a node!) + Field path = doc.getField("PATH"); + if (path == null || path.stringValue().length() == 0) + { + deletedDocuments.set(hits.id(i)); + // There should only be one thing to delete + // break; + } } } } diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 1408bad338..5d5220f821 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-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -20,6 +20,7 @@ package org.alfresco.repo.security.person; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -28,10 +29,12 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryFactory; import org.alfresco.query.CannedQueryResults; import org.alfresco.query.PagingRequest; @@ -46,9 +49,9 @@ import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy; import org.alfresco.repo.node.getchildren.FilterProp; import org.alfresco.repo.node.getchildren.FilterPropString; +import org.alfresco.repo.node.getchildren.FilterPropString.FilterTypeString; import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.node.getchildren.GetChildrenCannedQueryFactory; -import org.alfresco.repo.node.getchildren.FilterPropString.FilterTypeString; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.search.SearcherException; @@ -59,16 +62,16 @@ import org.alfresco.repo.security.permissions.PermissionServiceSPI; import org.alfresco.repo.tenant.TenantDomainMismatchException; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionListenerAdapter; -import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.transaction.TransactionListenerAdapter; +import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.admin.RepoAdminService; -import org.alfresco.service.cmr.admin.RepoUsageStatus; import org.alfresco.service.cmr.admin.RepoUsage.UsageType; +import org.alfresco.service.cmr.admin.RepoUsageStatus; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.invitation.InvitationException; import org.alfresco.service.cmr.model.FileFolderService; @@ -78,6 +81,9 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; @@ -161,7 +167,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per private JavaBehaviour beforeDeleteNodeValidationBehaviour; private boolean homeFolderCreationEager; - + static { Set props = new HashSet(); @@ -1192,43 +1198,59 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per ParameterCheck.mandatory("pagingRequest", pagingRequest); Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); - - NodeRef contextNodeRef = getPeopleContainer(); - - Set childTypeQNames = new HashSet(1); - childTypeQNames.add(ContentModel.TYPE_PERSON); - - // get canned query - GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST); - - List filterProps = null; - if (stringPropFilters != null) + + // TODO Remove this ALF-14127 hot fix code (calling nonCannedGetPeopleQuery(...) once this canned query does not fetch all rows from the database, + // which is very slow when there are a lot of users. 10,000 user takes about 4 seconds. The customer has 90,000. + CannedQueryResults cqResults = null; + String searchValue = null; + if (filterIgnoreCase && pagingRequest != null && pagingRequest.getQueryExecutionId() == null && pagingRequest.getSkipCount() == 0) { - filterProps = new ArrayList(stringPropFilters.size()); - for (Pair filterProp : stringPropFilters) - { - String filterStr = filterProp.getSecond(); - if ((filterStr == null) || (filterStr.equals("")) || (filterStr.equals("*"))) - { - // The wildcard means no filtering is needed on this property - continue; - } - else if (filterStr.endsWith("*")) - { - // The trailing * is implicit - filterStr = filterStr.substring(0, filterStr.length()-1); - } - - // Turn this into a canned query filter - filterProps.add(new FilterPropString(filterProp.getFirst(), filterStr, (filterIgnoreCase ? FilterTypeString.STARTSWITH_IGNORECASE : FilterTypeString.STARTSWITH))); - } + searchValue = getSearchOnNameValue(stringPropFilters); } + if (searchValue != null) + { + cqResults = nonCannedGetPeopleQuery(searchValue, pagingRequest); + } + else + { + NodeRef contextNodeRef = getPeopleContainer(); + + Set childTypeQNames = new HashSet(1); + childTypeQNames.add(ContentModel.TYPE_PERSON); + + // get canned query + GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST); + + List filterProps = null; + if (stringPropFilters != null) + { + filterProps = new ArrayList(stringPropFilters.size()); + for (Pair filterProp : stringPropFilters) + { + String filterStr = filterProp.getSecond(); + if ((filterStr == null) || (filterStr.equals("")) || (filterStr.equals("*"))) + { + // The wildcard means no filtering is needed on this property + continue; + } + else if (filterStr.endsWith("*")) + { + // The trailing * is implicit + filterStr = filterStr.substring(0, filterStr.length()-1); + } + + // Turn this into a canned query filter + filterProps.add(new FilterPropString(filterProp.getFirst(), filterStr, (filterIgnoreCase ? FilterTypeString.STARTSWITH_IGNORECASE : FilterTypeString.STARTSWITH))); + } + } GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(contextNodeRef, null, null, childTypeQNames, filterProps, sortProps, pagingRequest); - // execute canned query - final CannedQueryResults results = cq.execute(); + // execute canned query + cqResults = cq.execute(); + } + final CannedQueryResults results = cqResults; final List nodeRefs; if (results.getPageCount() > 0) { @@ -1303,6 +1325,258 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per }; } + /** + * If the search is on first, last and user name only with the same value, return that value, + * otherwise return null. + */ + // TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database. + private String getSearchOnNameValue(List> stringPropFilters) + { + String filter = null; + if (stringPropFilters != null && stringPropFilters.size() == 3) + { + // Does not check we don't have duplicates. + for (int i=0; i < 3; i++) + { + Pair pair = stringPropFilters.get(i); + if (i == 0) + { + filter = pair.getSecond().trim(); + if (filter == null || filter.length() == 0) + { + filter = null; + break; + } + } + + if ((i != 0 && !filter.equals(pair.getSecond().trim()) || !NAME_SEARCH_NAMES.contains(pair.getFirst()))) + { + filter = null; + break; + } + } + } + return filter; + } + + // TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database. + private static final List NAME_SEARCH_NAMES = Arrays.asList(new QName[] { + ContentModel.PROP_FIRSTNAME, ContentModel.PROP_LASTNAME, ContentModel.PROP_USERNAME}); + + /** + * Use Solr search based on code in org.alfresco.repo.jscript.People.getPeople(String, int) + */ + // TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database. + private CannedQueryResults nonCannedGetPeopleQuery(String filter, PagingRequest pagingRequest) + { + Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); + int maxResults = pagingRequest != null ? pagingRequest.getMaxItems() : Integer.MAX_VALUE; + if (maxResults <= 0) + { + maxResults = Integer.MAX_VALUE; + } + + String term = filter.replace("\\", "").replace("\"", ""); + StringTokenizer t = new StringTokenizer(term, " "); + int propIndex = term.indexOf(':'); + + SearchParameters params = new SearchParameters(); + params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName"); + params.setDefaultFieldName("_PERSON"); + + StringBuilder query = new StringBuilder(256); + + query.append("TYPE:\"").append(ContentModel.TYPE_PERSON).append("\" AND ("); + + if (t.countTokens() == 1) + { + // single word with no field will go against _PERSON and expand + + // fts-alfresco property search i.e. location:"maidenhead" + query.append(term.substring(0, propIndex + 1)).append('"') + .append(term.substring(propIndex + 1)); + if (propIndex > 0) + { + query.append('"'); + } + else + { + query.append("*\""); + } + } + else + { + // scan for non-fts-alfresco property search tokens + int nonFtsTokens = 0; + while (t.hasMoreTokens()) + { + if (t.nextToken().indexOf(':') == -1) + nonFtsTokens++; + } + t = new StringTokenizer(term, " "); + + // multiple terms supplied - look for first and second name etc. + // assume first term is first name, any more are second i.e. + // "Fraun van de Wiels" + // also allow fts-alfresco property search to reduce results + params.setDefaultOperator(SearchParameters.Operator.AND); + boolean firstToken = true; + boolean tokenSurname = false; + boolean propertySearch = false; + while (t.hasMoreTokens()) + { + term = t.nextToken(); + if (!propertySearch && term.indexOf(':') == -1) + { + if (nonFtsTokens == 1) + { + // simple search: first name, last name and username + // starting with term + query.append("_PERSON:\""); + query.append(term); + query.append("*\" "); + } + else + { + if (firstToken) + { + query.append("firstName:\""); + query.append(term); + query.append("*\" "); + + firstToken = false; + } + else + { + if (tokenSurname) + { + query.append("OR "); + } + query.append("lastName:\""); + query.append(term); + query.append("*\" "); + + tokenSurname = true; + } + } + } + else + { + // fts-alfresco property search i.e. "location:maidenhead" + propIndex = term.indexOf(':'); + query.append(term.substring(0, propIndex + 1)).append('"') + .append(term.substring(propIndex + 1)).append('"'); + + propertySearch = true; + } + } + } + query.append(")"); + + // define the search parameters + params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); + params.addStore(this.storeRef); + params.setQuery(query.toString()); + if (maxResults > 0) + { + params.setLimitBy(LimitBy.FINAL_SIZE); + params.setLimit(maxResults); + } + + ResultSet results = null; + List resultNodeRefs = null; + try + { + results = searchService.query(params); + resultNodeRefs = results.getNodeRefs(); + } + catch (Throwable err) + { + resultNodeRefs = Collections.emptyList(); + + // hide query parse error from users + if (logger.isDebugEnabled()) + logger.debug("Failed to execute people search: " + query.toString(), err); + } + finally + { + if (results != null) + { + results.close(); + } + } + + // Turn NodeRefs into a single page of results. + final List nodRefs = resultNodeRefs; + CannedQueryResults cqResults = new CannedQueryResults() + { + @Override + public CannedQuery getOriginatingQuery() + { + return null; + } + + @Override + public String getQueryExecutionId() + { + return null; + } + + @Override + public Pair getTotalResultCount() + { + int size = nodRefs.size(); + return new Pair(size, size); + } + + @Override + public int getPagedResultCount() + { + return nodRefs.size(); + } + + @Override + public int getPageCount() + { + return 1; + } + + @Override + public NodeRef getSingleResult() + { + if (nodRefs.size() != 1) + { + throw new IllegalStateException( + "There must be exactly one page of one result available."); + } + return nodRefs.get(0); + } + + @Override + public List getPage() + { + return nodRefs; + } + + @Override + public List> getPages() + { + return Collections.singletonList(getPage()); + } + + @Override + public boolean hasMoreItems() + { + return false; + } + }; + if (logger.isDebugEnabled()) + { + logger.debug("nonCannedGetPeopleQuery(\""+filter+"\", "+maxResults+") "+cqResults.getTotalResultCount()+" in "+(System.currentTimeMillis()-start)+" msecs "); + } + return cqResults; + } + /** * {@inheritDoc} */ @@ -1668,8 +1942,8 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per public int countPeople() { - NodeRef peopleContainer = getPeopleContainer(); - return nodeService.countChildAssocs(peopleContainer, true); + NodeRef peopleContainer = getPeopleContainer(); + return nodeService.countChildAssocs(peopleContainer, true); } /** diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 116e3ed815..66ab9d55a9 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -561,15 +561,17 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic } // Create the site's groups - String siteGroup = authorityService.createAuthority( - AuthorityType.GROUP, getSiteGroup(shortName, false), shortName, shareZones); + String siteGroupShortName = getSiteGroup(shortName, false); + String siteGroup = authorityService.createAuthority(AuthorityType.GROUP, siteGroupShortName, + siteGroupShortName, shareZones); QName siteType = directNodeService.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); + String permissionGroupShortName = getSiteRoleGroup(shortName, permission, false); + String permissionGroup = authorityService.createAuthority(AuthorityType.GROUP, + permissionGroupShortName, permissionGroupShortName, shareZones); authorityService.addAuthority(siteGroup, permissionGroup); // add any supplied memberships to it diff --git a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java index 91ecab05d3..1bfbfa4f6a 100644 --- a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java +++ b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java @@ -133,51 +133,67 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest public void testDelete() { - setDefaultRollback(false); - String uuid = GUID.generate(); - ChildAssociationRef childAssoc; - startNewTransaction(); - try - { - ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, - "/app:company_home"); - assertEquals(1, rs.length()); - NodeRef companyHome = rs.getNodeRef(0); - Map props = new HashMap(); - props.put(ContentModel.PROP_NAME, uuid); - childAssoc = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.APP_MODEL_1_0_URI, uuid), ContentModel.TYPE_CONTENT, props); - } - finally - { - endTransaction(); - } + log.debug("start testDelete"); - startNewTransaction(); - try + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + final String uuid = GUID.generate(); + class TestContext { - nodeService.deleteNode(childAssoc.getChildRef()); - } - finally - { - endTransaction(); - } + ChildAssociationRef childAssoc; + }; - startNewTransaction(); - try + RetryingTransactionCallback setupCB = new RetryingTransactionCallback() { - log.debug("Test that original node no longer exists..."); - assertFalse(nodeService.exists(childAssoc.getChildRef())); - log.debug("PASS - Original node no longer exists."); - NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, childAssoc.getChildRef().getId()); - log.debug("Test that archive node exists..."); - assertTrue(nodeService.exists(archiveNodeRef)); - log.debug("PASS - Archive node exists."); - } - finally + + @Override + public TestContext execute() throws Throwable + { + TestContext tc = new TestContext(); + ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, + "/app:company_home"); + assertEquals(1, rs.length()); + NodeRef companyHome = rs.getNodeRef(0); + Map props = new HashMap(); + props.put(ContentModel.PROP_NAME, uuid); + tc.childAssoc = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.APP_MODEL_1_0_URI, uuid), ContentModel.TYPE_CONTENT, props); + return tc; + } + }; + + final TestContext tc = tran.doInTransaction(setupCB, false, true); + + RetryingTransactionCallback deleteCB = new RetryingTransactionCallback() { - endTransaction(); - } + + @Override + public Void execute() throws Throwable + { + nodeService.deleteNode(tc.childAssoc.getChildRef()); + return null; + } + }; + + tran.doInTransaction(deleteCB, false, true); + + RetryingTransactionCallback validateCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + log.debug("Test that original node no longer exists..."); + assertFalse(nodeService.exists(tc.childAssoc.getChildRef())); + log.debug("PASS - Original node no longer exists."); + NodeRef archiveNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, tc.childAssoc.getChildRef().getId()); + log.debug("Test that archive node exists..."); + assertTrue(nodeService.exists(archiveNodeRef)); + log.debug("PASS - Archive node exists."); + return null; + } + }; + + tran.doInTransaction(validateCB, false, true); } @@ -185,9 +201,9 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest * Tests start and end with regard to locking. * @throws Exception */ - public void testStartAndEnd() throws Exception + public void DISABLED_testStartAndEnd() throws Exception { - log.info("testStartAndEnd"); + log.debug("start testStartAndEnd"); RetryingTransactionHelper trx = transactionService.getRetryingTransactionHelper(); @@ -199,6 +215,7 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest { log.debug("about to call start"); String transferId = receiver.start("1234", true, receiver.getVersion()); + File stagingFolder = null; try { @@ -267,6 +284,7 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest for (int i = 0; i < 5; i++) { log.info("test iteration:" + i); + trx.doInTransaction(cb, false, true); } } @@ -282,9 +300,9 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest * Going to cut down the timeout to a very short period, the lock should expire * @throws Exception */ - public void testLockTimeout() throws Exception + public void DISABLED_testLockTimeout() throws Exception { - log.info("testStartAndEnd"); + log.info("start testLockTimeout"); RetryingTransactionHelper trx = transactionService.getRetryingTransactionHelper(); @@ -310,16 +328,8 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest log.debug("about to call start"); String transferId = receiver.start("1234", true, receiver.getVersion()); Thread.sleep(1000); - try - { - receiver.saveSnapshot(transferId, null); - fail("did not timeout"); - } - catch (TransferException te) - { - logger.debug("expected to timeout", te); - // expect to go here with a timeout - } + receiver.saveSnapshot(transferId, null); + fail("did not timeout"); return null; } }; @@ -343,18 +353,30 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest trx.doInTransaction(startWithoutAnythingElse, false, true); Thread.sleep(1000); } - trx.doInTransaction(slowTransfer, false, true); + try + { + trx.doInTransaction(slowTransfer, false, true); + } + catch (Exception e) + { + // Expect to go here. + } } finally { receiver.setLockRefreshTime(lockRefreshTime); receiver.setLockTimeOut(lockTimeOut); } + + log.info("end testLockTimeout"); } public void testSaveContent() throws Exception { - log.info("testSaveContent"); + log.info("start testSaveContent"); + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + startNewTransaction(); try { @@ -380,7 +402,10 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest public void testSaveSnapshot() throws Exception { - log.info("testSaveSnapshot"); + log.info("start testSaveSnapshot"); + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + startNewTransaction(); try { @@ -388,7 +413,7 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest File snapshotFile = null; try { - TransferManifestNode node = createContentNode(transferId); + TransferManifestNode node = createContentNode(); List nodes = new ArrayList(); nodes.add(node); String snapshot = createSnapshot(nodes); @@ -417,140 +442,252 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest public void testBasicCommit() throws Exception { - log.info("testBasicCommit"); - startNewTransaction(); - TransferManifestNode node = null; - - try + log.info("start testBasicCommit"); + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + class TestContext { - String transferId = receiver.start("1234", true, receiver.getVersion()); - try + TransferManifestNode node = null; + String transferId = null; + } + + RetryingTransactionCallback setupCB = new RetryingTransactionCallback() + { + @Override + public TestContext execute() throws Throwable { - node = createContentNode(transferId); + TestContext tc = new TestContext(); + tc.node = createContentNode(); + return tc; + } + }; + + final TestContext tc = tran.doInTransaction(setupCB, false, true); + + RetryingTransactionCallback doPrepareCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + List nodes = new ArrayList(); - nodes.add(node); + nodes.add(tc.node); String snapshot = createSnapshot(nodes); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); - receiver.commit(transferId); - + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + receiver.saveContent(tc.transferId, tc.node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); + + return null; } - catch (Exception ex) - { - receiver.end(transferId); - throw ex; - } - } - finally + }; + + RetryingTransactionCallback doCommitCB = new RetryingTransactionCallback() { - endTransaction(); - } + @Override + public Void execute() throws Throwable + { + receiver.commit(tc.transferId); - startNewTransaction(); + return null; + } + }; + + RetryingTransactionCallback doEndCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + receiver.end(tc.transferId); + return null; + } + }; + try { - assertTrue(nodeService.exists(node.getNodeRef())); - nodeService.deleteNode(node.getNodeRef()); + tran.doInTransaction(doPrepareCB, false, true); + tran.doInTransaction(doCommitCB, false, true); } finally { - endTransaction(); + if(tc.transferId != null) + { + tran.doInTransaction(doEndCB, false, true); + } } + + RetryingTransactionCallback doValidateCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + assertTrue(nodeService.exists(tc.node.getNodeRef())); + nodeService.deleteNode(tc.node.getNodeRef()); + + return null; + } + }; + + tran.doInTransaction(doValidateCB, false, true); } + /** + * Test More Complex Commit + * + * @throws Exception + */ public void testMoreComplexCommit() throws Exception { - log.info("testMoreComplexCommit"); - List nodes = new ArrayList(); - TransferManifestNormalNode node1 = null; - TransferManifestNormalNode node2 = null; - TransferManifestNode node3 = null; - TransferManifestNode node4 = null; - TransferManifestNode node5 = null; - TransferManifestNode node6 = null; - TransferManifestNode node7 = null; - TransferManifestNode node8 = null; - TransferManifestNode node9 = null; - TransferManifestNode node10 = null; - TransferManifestNormalNode node11 = null; - TransferManifestNode node12 = null; - String transferId = null; + log.info("start testMoreComplexCommit"); + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + class TestContext + { + List nodes = new ArrayList(); + TransferManifestNormalNode node1 = null; + TransferManifestNormalNode node2 = null; + TransferManifestNode node3 = null; + TransferManifestNode node4 = null; + TransferManifestNode node5 = null; + TransferManifestNode node6 = null; + TransferManifestNode node7 = null; + TransferManifestNode node8 = null; + TransferManifestNode node9 = null; + TransferManifestNode node10 = null; + TransferManifestNormalNode node11 = null; + TransferManifestNode node12 = null; + String transferId = null; + }; - startNewTransaction(); + RetryingTransactionCallback setupCB = new RetryingTransactionCallback() + { + @Override + public TestContext execute() throws Throwable + { + TestContext tc = new TestContext(); + + tc.node1 = createContentNode(); + tc.nodes.add(tc.node1); + tc.node2 = createContentNode(); + tc.nodes.add(tc.node2); + tc.node3 = createContentNode(); + tc.nodes.add(tc.node3); + tc.node4 = createContentNode(); + tc.nodes.add(tc.node4); + tc.node5 = createContentNode(); + tc.nodes.add(tc.node5); + tc.node6 = createContentNode(); + tc.nodes.add(tc.node6); + tc.node7 = createContentNode(); + tc.nodes.add(tc.node7); + tc.node8 = createFolderNode(); + tc.nodes.add(tc.node8); + tc.node9 = createFolderNode(); + tc.nodes.add(tc.node9); + tc.node10 = createFolderNode(); + tc.nodes.add(tc.node10); + tc.node11 = createFolderNode(); + tc.nodes.add(tc.node11); + tc.node12 = createFolderNode(); + tc.nodes.add(tc.node12); + + associatePeers(tc.node1, tc.node2); + moveNode(tc.node2, tc.node11); + + return tc; + } + }; + + final TestContext tc = tran.doInTransaction(setupCB, false, true); + + RetryingTransactionCallback doPrepareCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + String snapshot = createSnapshot(tc.nodes); + + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + + for (TransferManifestNode node : tc.nodes) + { + receiver.saveContent(tc.transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); + } + return null; + } + }; + + RetryingTransactionCallback doCommitCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + log.info("testMoreComplexCommit - commit"); + receiver.commit(tc.transferId); + log.info("testMoreComplexCommit - commited"); + return null; + } + }; + RetryingTransactionCallback doEndCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // Needs to move elsewhere to allow other tests to pass. + receiver.end(tc.transferId); + return null; + } + }; + try { - transferId = receiver.start("1234", true, receiver.getVersion()); - node1 = createContentNode(transferId); - nodes.add(node1); - node2 = createContentNode(transferId); - nodes.add(node2); - node3 = createContentNode(transferId); - nodes.add(node3); - node4 = createContentNode(transferId); - nodes.add(node4); - node5 = createContentNode(transferId); - nodes.add(node5); - node6 = createContentNode(transferId); - nodes.add(node6); - node7 = createContentNode(transferId); - nodes.add(node7); - node8 = createFolderNode(transferId); - nodes.add(node8); - node9 = createFolderNode(transferId); - nodes.add(node9); - node10 = createFolderNode(transferId); - nodes.add(node10); - node11 = createFolderNode(transferId); - nodes.add(node11); - node12 = createFolderNode(transferId); - nodes.add(node12); - - associatePeers(node1, node2); - moveNode(node2, node11); - - String snapshot = createSnapshot(nodes); - - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - - for (TransferManifestNode node : nodes) - { - receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); - } - log.info("testMoreComplexCommit - commit"); - receiver.commit(transferId); - log.info("testMoreComplexCommit - commited"); - + tran.doInTransaction(doPrepareCB, false, true); + tran.doInTransaction(doCommitCB, false, true); } finally { - log.info("testMoreComplexCommit - end"); - receiver.end(transferId); - endTransaction(); - } - - startNewTransaction(); - try - { - log.info("testMoreComplexCommit - validate nodes"); - assertTrue(nodeService.getAspects(node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE)); - assertFalse(nodeService.getSourceAssocs(node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty()); - for (TransferManifestNode node : nodes) + if(tc.transferId != null) { - assertTrue(nodeService.exists(node.getNodeRef())); + tran.doInTransaction(doEndCB, false, true); } } - finally + + RetryingTransactionCallback validateCB = new RetryingTransactionCallback() { - endTransaction(); - } + @Override + public Void execute() throws Throwable + { + log.info("testMoreComplexCommit - validate nodes"); + assertTrue(nodeService.getAspects(tc.node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE)); + assertFalse(nodeService.getSourceAssocs(tc.node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty()); + for (TransferManifestNode node : tc.nodes) + { + assertTrue(nodeService.exists(node.getNodeRef())); + } + + return null; + } + }; + tran.doInTransaction(validateCB, false, true); } + /** + * Test Node Delete And Restore + * + * @throws Exception + */ @SuppressWarnings("unchecked") public void testNodeDeleteAndRestore() throws Exception { - TransferServicePolicies.OnEndInboundTransferPolicy mockedPolicyHandler = + log.info("start testNodeDeleteAndRestore"); + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + final TransferServicePolicies.OnEndInboundTransferPolicy mockedPolicyHandler = mock(TransferServicePolicies.OnEndInboundTransferPolicy.class); policyComponent.bindClassBehaviour( @@ -558,220 +695,359 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest TransferModel.TYPE_TRANSFER_RECORD, new JavaBehaviour(mockedPolicyHandler, "onEndInboundTransfer", NotificationFrequency.EVERY_EVENT)); - log.info("testNodeDeleteAndRestore"); - - setDefaultRollback(true); - startNewTransaction(); - String transferId = receiver.start("1234", true, receiver.getVersion()); - - List nodes = new ArrayList(); - TransferManifestNormalNode node1 = createContentNode(transferId); - nodes.add(node1); - TransferManifestNormalNode node2 = createContentNode(transferId); - nodes.add(node2); - TransferManifestNode node3 = createContentNode(transferId); - nodes.add(node3); - TransferManifestNode node4 = createContentNode(transferId); - nodes.add(node4); - TransferManifestNode node5 = createContentNode(transferId); - nodes.add(node5); - TransferManifestNode node6 = createContentNode(transferId); - nodes.add(node6); - TransferManifestNode node7 = createContentNode(transferId); - nodes.add(node7); - TransferManifestNode node8 = createFolderNode(transferId); - nodes.add(node8); - TransferManifestNode node9 = createFolderNode(transferId); - nodes.add(node9); - TransferManifestNode node10 = createFolderNode(transferId); - nodes.add(node10); - TransferManifestNormalNode node11 = createFolderNode(transferId); - nodes.add(node11); - TransferManifestNode node12 = createFolderNode(transferId); - nodes.add(node12); - - associatePeers(node1, node2); - moveNode(node2, node11); - - TransferManifestDeletedNode deletedNode8 = createDeletedNode(node8); - TransferManifestDeletedNode deletedNode2 = createDeletedNode(node2); - TransferManifestDeletedNode deletedNode11 = createDeletedNode(node11); - - endTransaction(); - - this.setDefaultRollback(false); - startNewTransaction(); - try + class TestContext { - String snapshot = createSnapshot(nodes); - log.debug(snapshot); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - - for (TransferManifestNode node : nodes) - { - receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); - } - receiver.commit(transferId); - - assertTrue(nodeService.getAspects(node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE)); - assertFalse(nodeService.getSourceAssocs(node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty()); - - ArgumentCaptor transferIdCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor createdNodesCaptor = ArgumentCaptor.forClass(Set.class); - ArgumentCaptor updatedNodesCaptor = ArgumentCaptor.forClass(Set.class); - ArgumentCaptor deletedNodesCaptor = ArgumentCaptor.forClass(Set.class); - verify(mockedPolicyHandler, times(1)).onEndInboundTransfer(transferIdCaptor.capture(), - createdNodesCaptor.capture(), updatedNodesCaptor.capture(), deletedNodesCaptor.capture()); - assertEquals(transferId, transferIdCaptor.getValue()); - Set capturedCreatedNodes = createdNodesCaptor.getValue(); - assertEquals(nodes.size(), capturedCreatedNodes.size()); - - for (TransferManifestNode node : nodes) - { - assertTrue(nodeService.exists(node.getNodeRef())); - assertTrue(capturedCreatedNodes.contains(node.getNodeRef())); - } - } - finally - { - endTransaction(); - } - - reset(mockedPolicyHandler); + String transferId; + TransferManifestNormalNode node1; + TransferManifestNormalNode node2; + TransferManifestNode node3; + TransferManifestNode node4; + TransferManifestNode node5; + TransferManifestNode node6; + TransferManifestNode node7; + TransferManifestNode node8; + TransferManifestNode node9; + TransferManifestNode node10; + TransferManifestNormalNode node11; + TransferManifestNode node12; + TransferManifestDeletedNode deletedNode8; + TransferManifestDeletedNode deletedNode2; + TransferManifestDeletedNode deletedNode11; + List nodes; + String errorMsgId; + }; - startNewTransaction(); - try + RetryingTransactionCallback setupCB = new RetryingTransactionCallback() { - // Now delete nodes 8, 2, and 11 (11 and 2 are parent/child) - transferId = receiver.start("1234", true, receiver.getVersion()); - String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { deletedNode8, deletedNode2, - deletedNode11 })); - log.debug(snapshot); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - receiver.commit(transferId); - - ArgumentCaptor transferIdCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor createdNodesCaptor = ArgumentCaptor.forClass(Set.class); - ArgumentCaptor updatedNodesCaptor = ArgumentCaptor.forClass(Set.class); - ArgumentCaptor deletedNodesCaptor = ArgumentCaptor.forClass(Set.class); - verify(mockedPolicyHandler, times(1)).onEndInboundTransfer(transferIdCaptor.capture(), - createdNodesCaptor.capture(), updatedNodesCaptor.capture(), deletedNodesCaptor.capture()); - assertEquals(transferId, transferIdCaptor.getValue()); - Set capturedDeletedNodes = deletedNodesCaptor.getValue(); - assertEquals(3, capturedDeletedNodes.size()); - assertTrue(capturedDeletedNodes.contains(deletedNode8.getNodeRef())); - assertTrue(capturedDeletedNodes.contains(deletedNode2.getNodeRef())); - assertTrue(capturedDeletedNodes.contains(deletedNode11.getNodeRef())); - } - finally - { - endTransaction(); - } - - startNewTransaction(); - try - { - log.debug("Test success of transfer..."); - TransferProgress progress = receiver.getProgressMonitor().getProgress(transferId); - assertEquals(TransferProgress.Status.COMPLETE, progress.getStatus()); - - NodeRef archiveNode8 = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node8.getNodeRef().getId()); - NodeRef archiveNode2 = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node2.getNodeRef().getId()); - NodeRef archiveNode11 = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, node11.getNodeRef().getId()); - - assertTrue(nodeService.exists(archiveNode8)); - assertTrue(nodeService.hasAspect(archiveNode8, ContentModel.ASPECT_ARCHIVED)); - log.debug("Successfully tested existence of archive node: " + archiveNode8); - - assertTrue(nodeService.exists(archiveNode2)); - assertTrue(nodeService.hasAspect(archiveNode2, ContentModel.ASPECT_ARCHIVED)); - log.debug("Successfully tested existence of archive node: " + archiveNode2); - - assertTrue(nodeService.exists(archiveNode11)); - assertTrue(nodeService.hasAspect(archiveNode11, ContentModel.ASPECT_ARCHIVED)); - log.debug("Successfully tested existence of archive node: " + archiveNode11); - - log.debug("Successfully tested existence of all archive nodes"); - - log.debug("Testing existence of original node: " + node8.getNodeRef()); - assertFalse(nodeService.exists(node8.getNodeRef())); - - log.debug("Testing existence of original node: " + node2.getNodeRef()); - assertFalse(nodeService.exists(node2.getNodeRef())); - - log.debug("Testing existence of original node: " + node11.getNodeRef()); - assertFalse(nodeService.exists(node11.getNodeRef())); - - log.debug("Successfully tested non-existence of all original nodes"); - - log.debug("Progress indication: " + progress.getCurrentPosition() + "/" + progress.getEndPosition()); - } - finally - { - endTransaction(); - } - System.out.println("Now try to restore orphan node 2."); - - reset(mockedPolicyHandler); - - String errorMsgId = null; - startNewTransaction(); - try - { - // try to restore node 2. Expect an "orphan" failure, since its parent (node11) is deleted - transferId = receiver.start("1234", true, receiver.getVersion()); - String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { node2 })); - log.debug(snapshot); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - receiver.saveContent(transferId, node2.getUuid(), new ByteArrayInputStream(dummyContentBytes)); - try + @Override + public TestContext execute() throws Throwable { - receiver.commit(transferId); - fail("Expected an exception"); + TestContext tc = new TestContext(); + + tc.nodes = new ArrayList(); + tc.node1 = createContentNode(); + tc.nodes.add(tc.node1); + tc.node2 = createContentNode(); + tc.nodes.add(tc.node2); + tc.node3 = createContentNode(); + tc.nodes.add(tc.node3); + tc.node4 = createContentNode(); + tc.nodes.add(tc.node4); + tc.node5 = createContentNode(); + tc.nodes.add(tc.node5); + tc.node6 = createContentNode(); + tc.nodes.add(tc.node6); + tc.node7 = createContentNode(); + tc.nodes.add(tc.node7); + tc.node8 = createFolderNode(); + tc.nodes.add(tc.node8); + tc.node9 = createFolderNode(); + tc.nodes.add(tc.node9); + tc.node10 = createFolderNode(); + tc.nodes.add(tc.node10); + tc.node11 = createFolderNode(); + tc.nodes.add(tc.node11); + tc.node12 = createFolderNode(); + tc.nodes.add(tc.node12); + + associatePeers(tc.node1, tc.node2); + moveNode(tc.node2, tc.node11); + + tc.deletedNode8 = createDeletedNode(tc.node8); + tc.deletedNode2 = createDeletedNode(tc.node2); + tc.deletedNode11 = createDeletedNode(tc.node11); + + return tc; } - catch (TransferException ex) + }; + + final TestContext tc = tran.doInTransaction(setupCB, false, true); + + RetryingTransactionCallback doFirstPrepareCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable { - // Expected - errorMsgId = ex.getMsgId(); + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + String snapshot = createSnapshot(tc.nodes); + log.debug(snapshot); + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + + for (TransferManifestNode node : tc.nodes) + { + receiver.saveContent(tc.transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); + } + + return null; + } + }; + + RetryingTransactionCallback doCommitCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + receiver.commit(tc.transferId); + + return null; + } + }; + RetryingTransactionCallback doEndCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + // Needs to move elsewhere to allow other tests to pass. + receiver.end(tc.transferId); + return null; + } + }; + + RetryingTransactionCallback validateFirstCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + assertTrue(nodeService.getAspects(tc.node1.getNodeRef()).contains(ContentModel.ASPECT_ATTACHABLE)); + assertFalse(nodeService.getSourceAssocs(tc.node2.getNodeRef(), ContentModel.ASSOC_ATTACHMENTS).isEmpty()); ArgumentCaptor transferIdCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor createdNodesCaptor = ArgumentCaptor.forClass(Set.class); ArgumentCaptor updatedNodesCaptor = ArgumentCaptor.forClass(Set.class); ArgumentCaptor deletedNodesCaptor = ArgumentCaptor.forClass(Set.class); - verify(mockedPolicyHandler, times(1)).onEndInboundTransfer(transferIdCaptor.capture(), - createdNodesCaptor.capture(), updatedNodesCaptor.capture(), deletedNodesCaptor.capture()); - - assertEquals(transferId, transferIdCaptor.getValue()); - assertTrue(createdNodesCaptor.getValue().isEmpty()); - assertTrue(updatedNodesCaptor.getValue().isEmpty()); - assertTrue(deletedNodesCaptor.getValue().isEmpty()); - } - } - catch (Exception ex) - { - receiver.end(transferId); - throw ex; - } - finally - { - endTransaction(); - } + createdNodesCaptor.capture(), + updatedNodesCaptor.capture(), + deletedNodesCaptor.capture()); + assertEquals(tc.transferId, transferIdCaptor.getValue()); + Set capturedCreatedNodes = createdNodesCaptor.getValue(); + assertEquals(tc.nodes.size(), capturedCreatedNodes.size()); - startNewTransaction(); + for (TransferManifestNode node : tc.nodes) + { + assertTrue(nodeService.exists(node.getNodeRef())); + assertTrue(capturedCreatedNodes.contains(node.getNodeRef())); + } + return null; + } + }; + + + /** + * First transfer test here + */ + reset(mockedPolicyHandler); try { - TransferProgress progress = receiver.getProgressMonitor().getProgress(transferId); - assertEquals(TransferProgress.Status.ERROR, progress.getStatus()); - log.debug("Progress indication: " + progress.getCurrentPosition() + "/" + progress.getEndPosition()); - assertNotNull("Progress error", progress.getError()); - assertTrue(progress.getError() instanceof Exception); - assertTrue(errorMsgId, errorMsgId.contains("orphan")); + tran.doInTransaction(doFirstPrepareCB, false, true); + tran.doInTransaction(doCommitCB, false, true); + tran.doInTransaction(validateFirstCB, false, true); } finally { - endTransaction(); + if(tc.transferId != null) + { + tran.doInTransaction(doEndCB, false, true); + } } + + /** + * Second transfer this time with some deleted nodes, + * + * nodes 8, 2, and 11 (11 and 2 are parent/child) + */ + reset(mockedPolicyHandler); + + logger.debug("part 2 - transfer some deleted nodes"); + + RetryingTransactionCallback doSecondPrepareCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + // Now delete nodes 8, 2, and 11 (11 and 2 are parent/child) + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { tc.deletedNode8, + tc.deletedNode2, + tc.deletedNode11 })); + log.debug(snapshot); + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + + return null; + } + }; + + RetryingTransactionCallback validateSecondCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ArgumentCaptor transferIdCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor createdNodesCaptor = ArgumentCaptor.forClass(Set.class); + ArgumentCaptor updatedNodesCaptor = ArgumentCaptor.forClass(Set.class); + ArgumentCaptor deletedNodesCaptor = ArgumentCaptor.forClass(Set.class); + verify(mockedPolicyHandler, times(1)).onEndInboundTransfer(transferIdCaptor.capture(), + createdNodesCaptor.capture(), + updatedNodesCaptor.capture(), + deletedNodesCaptor.capture()); + assertEquals(tc.transferId, transferIdCaptor.getValue()); + Set capturedDeletedNodes = deletedNodesCaptor.getValue(); + assertEquals(3, capturedDeletedNodes.size()); + assertTrue(capturedDeletedNodes.contains(tc.deletedNode8.getNodeRef())); + assertTrue(capturedDeletedNodes.contains(tc.deletedNode2.getNodeRef())); + assertTrue(capturedDeletedNodes.contains(tc.deletedNode11.getNodeRef())); + + log.debug("Test success of transfer..."); + TransferProgress progress = receiver.getProgressMonitor().getProgress(tc.transferId); + assertEquals(TransferProgress.Status.COMPLETE, progress.getStatus()); + + NodeRef archiveNode8 = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, tc.node8.getNodeRef().getId()); + NodeRef archiveNode2 = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, tc.node2.getNodeRef().getId()); + NodeRef archiveNode11 = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, tc.node11.getNodeRef().getId()); + + assertTrue(nodeService.exists(archiveNode8)); + assertTrue(nodeService.hasAspect(archiveNode8, ContentModel.ASPECT_ARCHIVED)); + log.debug("Successfully tested existence of archive node: " + archiveNode8); + + assertTrue(nodeService.exists(archiveNode2)); + assertTrue(nodeService.hasAspect(archiveNode2, ContentModel.ASPECT_ARCHIVED)); + log.debug("Successfully tested existence of archive node: " + archiveNode2); + + assertTrue(nodeService.exists(archiveNode11)); + assertTrue(nodeService.hasAspect(archiveNode11, ContentModel.ASPECT_ARCHIVED)); + log.debug("Successfully tested existence of archive node: " + archiveNode11); + + log.debug("Successfully tested existence of all archive nodes"); + + log.debug("Testing existence of original node: " + tc.node8.getNodeRef()); + assertFalse(nodeService.exists(tc.node8.getNodeRef())); + + log.debug("Testing existence of original node: " + tc.node2.getNodeRef()); + assertFalse(nodeService.exists(tc.node2.getNodeRef())); + + log.debug("Testing existence of original node: " + tc.node11.getNodeRef()); + assertFalse(nodeService.exists(tc.node11.getNodeRef())); + + log.debug("Successfully tested non-existence of all original nodes"); + + log.debug("Progress indication: " + progress.getCurrentPosition() + "/" + progress.getEndPosition()); + return null; + } + }; + + try + { + tran.doInTransaction(doSecondPrepareCB, false, true); + tran.doInTransaction(doCommitCB, false, true); + tran.doInTransaction(validateSecondCB, false, true); + } + finally + { + if(tc.transferId != null) + { + tran.doInTransaction(doEndCB, false, true); + } + } + + logger.debug("part 3 - restore orphan node which should fail"); + System.out.println("Now try to restore orphan node 2."); + /** + * A third transfer. Expect an "orphan" failure, since its parent (node11) is deleted + */ + reset(mockedPolicyHandler); + + String errorMsgId = null; + + RetryingTransactionCallback doThirdPrepareCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { tc.node2 })); + log.debug(snapshot); + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + receiver.saveContent(tc.transferId, tc.node2.getUuid(), new ByteArrayInputStream(dummyContentBytes)); + + return null; + } + }; + + + RetryingTransactionCallback doCommitExpectingFailCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + try + { + receiver.commit(tc.transferId); + fail("Expected an exception"); + } + catch (TransferException ex) + { + // Expected + tc.errorMsgId = ex.getMsgId(); + + ArgumentCaptor transferIdCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor createdNodesCaptor = ArgumentCaptor.forClass(Set.class); + ArgumentCaptor updatedNodesCaptor = ArgumentCaptor.forClass(Set.class); + ArgumentCaptor deletedNodesCaptor = ArgumentCaptor.forClass(Set.class); + + verify(mockedPolicyHandler, times(1)).onEndInboundTransfer(transferIdCaptor.capture(), + createdNodesCaptor.capture(), updatedNodesCaptor.capture(), deletedNodesCaptor.capture()); + + assertEquals(tc.transferId, transferIdCaptor.getValue()); + assertTrue(createdNodesCaptor.getValue().isEmpty()); + assertTrue(updatedNodesCaptor.getValue().isEmpty()); + assertTrue(deletedNodesCaptor.getValue().isEmpty()); + } + + + return null; + } + }; + + RetryingTransactionCallback validateThirdCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + TransferProgress progress = receiver.getProgressMonitor().getProgress(tc.transferId); + assertEquals(TransferProgress.Status.ERROR, progress.getStatus()); + log.debug("Progress indication: " + progress.getCurrentPosition() + "/" + progress.getEndPosition()); + assertNotNull("Progress error", progress.getError()); + assertTrue(progress.getError() instanceof Exception); + assertTrue(tc.errorMsgId, tc.errorMsgId.contains("orphan")); + return null; + } + }; + + try + { + tran.doInTransaction(doThirdPrepareCB, false, true); + tran.doInTransaction(doCommitExpectingFailCB, false, true); + } + finally + { + if(tc.transferId != null) + { + tran.doInTransaction(doEndCB, false, true); + } + } + + tran.doInTransaction(validateThirdCB, false, true); + + log.debug("start testNodeDeleteAndRestore"); + } /** @@ -808,115 +1084,206 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest */ public void testJira_ALF_2772() throws Exception { - setDefaultRollback(true); - startNewTransaction(); - String transferId = receiver.start("1234", true, receiver.getVersion()); - - TransferManifestNormalNode node1 = createContentNode(transferId); - TransferManifestNormalNode node2 = createContentNode(transferId); - TransferManifestNormalNode node11 = createFolderNode(transferId); - - associatePeers(node1, node2); - moveNode(node2, node11); - - TransferManifestDeletedNode deletedNode11 = createDeletedNode(node11); - - endTransaction(); - - List nodes = new ArrayList(); + log.debug("start testJira_ALF_2772"); + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + class TestContext + { + TransferManifestNormalNode node1 = null; + TransferManifestNormalNode node2 = null; + TransferManifestNormalNode node11 = null; + TransferManifestDeletedNode deletedNode11 = null; + String transferId = null; + }; - //First we'll just send a folder node - nodes.add(node11); + RetryingTransactionCallback setupCB = new RetryingTransactionCallback() + { + @Override + public TestContext execute() throws Throwable + { + TestContext tc = new TestContext(); + + tc.node1 = createContentNode(); + tc.node2 = createContentNode(); + tc.node11 = createFolderNode(); + + associatePeers(tc.node1, tc.node2); + moveNode(tc.node2, tc.node11); + + tc.deletedNode11 = createDeletedNode(tc.node11); + + return tc; + } + }; - this.setDefaultRollback(false); - startNewTransaction(); + final TestContext tc = tran.doInTransaction(setupCB, false, true); + + RetryingTransactionCallback doEndCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + // Needs to move elsewhere to allow other tests to pass. + receiver.end(tc.transferId); + return null; + } + }; + + + RetryingTransactionCallback doFirstCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + + List nodes = new ArrayList(); + + //First we'll just send a folder node + nodes.add(tc.node11); + + String snapshot = createSnapshot(nodes); + log.debug(snapshot); + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + + for (TransferManifestNode node : nodes) + { + receiver.saveContent(tc.transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); + } + receiver.commit(tc.transferId); + + for (TransferManifestNode node : nodes) + { + assertTrue(nodeService.exists(node.getNodeRef())); + } + + return null; + } + }; + + /** + * First we'll just send a folder node + */ try { - String snapshot = createSnapshot(nodes); - log.debug(snapshot); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - - for (TransferManifestNode node : nodes) + tran.doInTransaction(doFirstCB, false, true); + } + finally + { + if(tc.transferId != null) { - receiver.saveContent(transferId, node.getUuid(), new ByteArrayInputStream(dummyContentBytes)); - } - receiver.commit(transferId); - - for (TransferManifestNode node : nodes) - { - assertTrue(nodeService.exists(node.getNodeRef())); + tran.doInTransaction(doEndCB, false, true); } } - finally + + + + RetryingTransactionCallback doSecondCB = new RetryingTransactionCallback() { - receiver.end(transferId); - endTransaction(); - } + @Override + public Void execute() throws Throwable + { + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { tc.deletedNode11 })); + log.debug(snapshot); + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + receiver.commit(tc.transferId); + + return null; + } + }; + + RetryingTransactionCallback doValidateSecondCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + TransferProgress progress = receiver.getProgressMonitor().getProgress(tc.transferId); + assertEquals(TransferProgress.Status.COMPLETE, progress.getStatus()); - //Now we delete the folder - startNewTransaction(); + NodeRef archivedNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, tc.deletedNode11.getNodeRef().getId()); + assertTrue(nodeService.exists(archivedNodeRef)); + assertTrue(nodeService.hasAspect(archivedNodeRef, ContentModel.ASPECT_ARCHIVED)); + log.debug("Successfully tested existence of archive node: " + tc.deletedNode11.getNodeRef()); + + log.debug("Successfully tested existence of all archive nodes"); + + log.debug("Testing existence of original node: " + tc.node11.getNodeRef()); + assertFalse(nodeService.exists(tc.node11.getNodeRef())); + + log.debug("Successfully tested non-existence of all original nodes"); + + log.debug("Progress indication: " + progress.getCurrentPosition() + "/" + progress.getEndPosition()); + + return null; + } + }; + + /** + * Then delete a folder node + */ try { - transferId = receiver.start("1234", true, receiver.getVersion()); - String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { deletedNode11 })); - log.debug(snapshot); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - receiver.commit(transferId); + tran.doInTransaction(doSecondCB, false, true); + tran.doInTransaction(doValidateSecondCB, false, true); } finally { - receiver.end(transferId); - endTransaction(); + if(tc.transferId != null) + { + tran.doInTransaction(doEndCB, false, true); + } } + - startNewTransaction(); + /** + * Finally we transfer node2 and node11 (in that order) + */ + RetryingTransactionCallback doThirdCB = new RetryingTransactionCallback() + { + + @Override + public Void execute() throws Throwable + { + + tc.transferId = receiver.start("1234", true, receiver.getVersion()); + String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { tc.node2, tc.node11 })); + log.debug(snapshot); + receiver.saveSnapshot(tc.transferId, new StringInputStream(snapshot, "UTF-8")); + receiver.saveContent(tc.transferId, tc.node2.getUuid(), new ByteArrayInputStream(dummyContentBytes)); + receiver.commit(tc.transferId); + + return null; + } + }; + + RetryingTransactionCallback doValidateThirdCB = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + + return null; + } + }; + + /** + * Then delete a folder node + */ try { - log.debug("Test success of transfer..."); - TransferProgress progress = receiver.getProgressMonitor().getProgress(transferId); - assertEquals(TransferProgress.Status.COMPLETE, progress.getStatus()); - - NodeRef archivedNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, deletedNode11.getNodeRef().getId()); - assertTrue(nodeService.exists(archivedNodeRef)); - assertTrue(nodeService.hasAspect(archivedNodeRef, ContentModel.ASPECT_ARCHIVED)); - log.debug("Successfully tested existence of archive node: " + deletedNode11.getNodeRef()); - - log.debug("Successfully tested existence of all archive nodes"); - - log.debug("Testing existence of original node: " + node11.getNodeRef()); - assertFalse(nodeService.exists(node11.getNodeRef())); - - log.debug("Successfully tested non-existence of all original nodes"); - - log.debug("Progress indication: " + progress.getCurrentPosition() + "/" + progress.getEndPosition()); + tran.doInTransaction(doThirdCB, false, true); + tran.doInTransaction(doValidateThirdCB, false, true); } finally { - endTransaction(); - } - - - //Finally we transfer node2 and node11 (in that order) - startNewTransaction(); - try - { - transferId = receiver.start("1234", true, receiver.getVersion()); - String snapshot = createSnapshot(Arrays.asList(new TransferManifestNode[] { node2, node11 })); - log.debug(snapshot); - receiver.saveSnapshot(transferId, new StringInputStream(snapshot, "UTF-8")); - receiver.saveContent(transferId, node2.getUuid(), new ByteArrayInputStream(dummyContentBytes)); - receiver.commit(transferId); - } - catch (Exception ex) - { - fail("Test of ALF-2772 failed: " + ex.getMessage()); - } - finally - { - receiver.end(transferId); - endTransaction(); + if(tc.transferId != null) + { + tran.doInTransaction(doEndCB, false, true); + } } } @@ -924,9 +1291,10 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest public void testAsyncCommit() throws Exception { - log.info("testAsyncCommit"); + log.info("start testAsyncCommit"); this.setDefaultRollback(false); + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); startNewTransaction(); final String transferId = receiver.start("1234", true, receiver.getVersion()); @@ -934,29 +1302,29 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest startNewTransaction(); final List nodes = new ArrayList(); - final TransferManifestNormalNode node1 = createContentNode(transferId); + final TransferManifestNormalNode node1 = createContentNode(); nodes.add(node1); - final TransferManifestNormalNode node2 = createContentNode(transferId); + final TransferManifestNormalNode node2 = createContentNode(); nodes.add(node2); - TransferManifestNode node3 = createContentNode(transferId); + TransferManifestNode node3 = createContentNode(); nodes.add(node3); - TransferManifestNode node4 = createContentNode(transferId); + TransferManifestNode node4 = createContentNode(); nodes.add(node4); - TransferManifestNode node5 = createContentNode(transferId); + TransferManifestNode node5 = createContentNode(); nodes.add(node5); - TransferManifestNode node6 = createContentNode(transferId); + TransferManifestNode node6 = createContentNode(); nodes.add(node6); - TransferManifestNode node7 = createContentNode(transferId); + TransferManifestNode node7 = createContentNode(); nodes.add(node7); - TransferManifestNode node8 = createFolderNode(transferId); + TransferManifestNode node8 = createFolderNode(); nodes.add(node8); - TransferManifestNode node9 = createFolderNode(transferId); + TransferManifestNode node9 = createFolderNode(); nodes.add(node9); - TransferManifestNode node10 = createFolderNode(transferId); + TransferManifestNode node10 = createFolderNode(); nodes.add(node10); - TransferManifestNormalNode node11 = createFolderNode(transferId); + TransferManifestNormalNode node11 = createFolderNode(); nodes.add(node11); - TransferManifestNode node12 = createFolderNode(transferId); + TransferManifestNode node12 = createFolderNode(); nodes.add(node12); associatePeers(node1, node2); @@ -1110,7 +1478,7 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest /** * @return */ - private TransferManifestNormalNode createContentNode(String transferId) throws Exception + private TransferManifestNormalNode createContentNode(/*String transferId*/) throws Exception { TransferManifestNormalNode node = new TransferManifestNormalNode(); String uuid = GUID.generate(); @@ -1146,7 +1514,7 @@ public class RepoTransferReceiverImplTest extends BaseAlfrescoSpringTest return node; } - private TransferManifestNormalNode createFolderNode(String transferId) throws Exception + private TransferManifestNormalNode createFolderNode(/*String transferId*/) throws Exception { TransferManifestNormalNode node = new TransferManifestNormalNode(); String uuid = GUID.generate(); diff --git a/source/java/org/alfresco/repo/version/VersionableAspect.java b/source/java/org/alfresco/repo/version/VersionableAspect.java index 95ad61cf10..3d3551bb2a 100644 --- a/source/java/org/alfresco/repo/version/VersionableAspect.java +++ b/source/java/org/alfresco/repo/version/VersionableAspect.java @@ -453,7 +453,7 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate Map after) { if ((this.nodeService.exists(nodeRef) == true) && - !LockUtils.isLockedOrReadOnly(nodeRef, lockService) && + !LockUtils.isLockedAndReadOnly(nodeRef, lockService) && (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) && (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY) == false)) { diff --git a/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java b/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java index 3270727d7a..be00b81c2c 100644 --- a/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java +++ b/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java @@ -26,16 +26,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.Date; import java.util.HashMap; import java.util.List; +import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionDoesNotExistException; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionServiceException; import org.alfresco.util.EqualsHelper; -import org.alfresco.util.VersionNumber; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -242,22 +241,25 @@ public class VersionHistoryImpl implements VersionHistory public int compare(Version v1, Version v2) { - Date v1Date = v1.getFrozenModifiedDate(); - Date v2Date = v2.getFrozenModifiedDate(); int result = 0; - if ((v1Date != null) && (v2Date != null)) + + if ((null != v1) && (null != v2)) { - result = v2.getFrozenModifiedDate().compareTo(v1.getFrozenModifiedDate()); - } - else - { - logger.warn("Missing frozen modified date"); - } - - if (result == 0) - { - result = new VersionNumber(v2.getVersionLabel()).compareTo(new VersionNumber(v1.getVersionLabel())); + Serializable dbIdV1 = (null != v1.getVersionProperties()) ? (v1.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); + Serializable dbIdV2 = (null != v2.getVersionProperties()) ? (v2.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); + + if ((null != dbIdV1) && (null != dbIdV2)) + { + Long id1 = (dbIdV1 instanceof Integer) ? ((Integer) dbIdV1) : ((Long) dbIdV1); + Long id2 = (dbIdV2 instanceof Integer) ? ((Integer) dbIdV2) : ((Long) dbIdV2); + result = (id2).compareTo(id1); + } + else + { + logger.warn("DB Id of versioned node is missing!"); + } } + return result; } } @@ -273,22 +275,25 @@ public class VersionHistoryImpl implements VersionHistory public int compare(Version v1, Version v2) { - Date v1Date = v1.getFrozenModifiedDate(); - Date v2Date = v2.getFrozenModifiedDate(); int result = 0; - if ((v1Date != null) && (v2Date != null)) + + if ((null != v1) && (null != v2)) { - result = v1.getFrozenModifiedDate().compareTo(v2.getFrozenModifiedDate()); - } - else - { - logger.warn("Missing frozen modified date"); - } - - if (result == 0) - { - result = new VersionNumber(v1.getVersionLabel()).compareTo(new VersionNumber(v2.getVersionLabel())); + Serializable dbIdV1 = (null != v1.getVersionProperties()) ? (v1.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); + Serializable dbIdV2 = (null != v2.getVersionProperties()) ? (v2.getVersionProperties().get(ContentModel.PROP_NODE_DBID.getLocalName())) : (null); + + if ((null != dbIdV1) && (null != dbIdV2)) + { + Long id1 = (dbIdV1 instanceof Integer) ? ((Integer) dbIdV1) : ((Long) dbIdV1); + Long id2 = (dbIdV2 instanceof Integer) ? ((Integer) dbIdV2) : ((Long) dbIdV2); + result = (id1).compareTo(id2); + } + else + { + logger.warn("DB Id of versioned node is missing!"); + } } + return result; } } diff --git a/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java b/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java index 09ac31db82..d56e81638e 100644 --- a/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java +++ b/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java @@ -81,7 +81,9 @@ public interface CheckOutCheckInService * When a working copy is checked in the current state of the working copy is copied to the * original node. This will include any content updated in the working node. *

- * If version properties are provided the original node will be versioned and updated accordingly. + * If versioning is not enabled on a node (the versionable aspect is not present on the node), the + * check in overwrites the existing node and releases the lock unless the keepCheckedOut flag is used. + * With versioning enabled on the node, a new version is always created. *

* If a content Url is provided it will be used to update the content of the working node before the * checkin operation takes place. diff --git a/source/java/org/alfresco/service/cmr/rule/RuleService.java b/source/java/org/alfresco/service/cmr/rule/RuleService.java index 9fc799112b..526fe6190b 100644 --- a/source/java/org/alfresco/service/cmr/rule/RuleService.java +++ b/source/java/org/alfresco/service/cmr/rule/RuleService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -77,7 +77,7 @@ public interface RuleService public boolean isEnabled(); /** - * Indicates wether the rules for a given node are enabled or not. If the + * Indicates whether the rules for a given node are enabled or not. If the * rules are not enabled then they will not be executed. * * @param nodeRef the node reference diff --git a/source/java/org/alfresco/util/config/RepositoryFolderConfigBean.java b/source/java/org/alfresco/util/config/RepositoryFolderConfigBean.java index 8f436c30ef..26e37802d3 100644 --- a/source/java/org/alfresco/util/config/RepositoryFolderConfigBean.java +++ b/source/java/org/alfresco/util/config/RepositoryFolderConfigBean.java @@ -18,21 +18,20 @@ */ package org.alfresco.util.config; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; -import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; 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.search.SearchService; import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.PropertyCheck; /** @@ -45,45 +44,28 @@ import org.alfresco.util.PropertyCheck; */ public class RepositoryFolderConfigBean extends RepositoryPathConfigBean { - private List folderPath; + private String folderPath; public RepositoryFolderConfigBean() { - folderPath = Collections.emptyList(); + folderPath = ""; } @Override public String toString() { StringBuilder sb = new StringBuilder(128); - sb.append("Folder Path: ").append(super.getStoreRef()).append(super.getRootPath()); - for (String folder : folderPath) - { - sb.append("/").append(folder); - } + sb.append("Folder Path: ").append(super.getStoreRef()).append(super.getRootPath()).append("/").append(folderPath); return sb.toString(); } - /** - * Get the folder name path - */ - public List getFolderNames() - { - return folderPath; - } - /** * * @return Returns the string representation of the folder path */ public String getFolderPath() { - StringBuilder sb = new StringBuilder(56); - for (String pathElement : folderPath) - { - sb.append("/").append(pathElement); - } - return sb.toString(); + return folderPath; } /** @@ -95,10 +77,10 @@ public class RepositoryFolderConfigBean extends RepositoryPathConfigBean { if (!PropertyCheck.isValidPropertyString(folderPath)) { - folderPath = ""; + throw new IllegalArgumentException("Invalid folder name path for property 'folderPath': " + folderPath); } - this.folderPath = new ArrayList(5); StringTokenizer tokenizer = new StringTokenizer(folderPath, "/"); + StringBuilder pathBuff = new StringBuilder(folderPath.length()); while (tokenizer.hasMoreTokens()) { String folderName = tokenizer.nextToken(); @@ -106,8 +88,13 @@ public class RepositoryFolderConfigBean extends RepositoryPathConfigBean { throw new IllegalArgumentException("Invalid folder name path for property 'folderPath': " + folderPath); } - this.folderPath.add(folderName); + pathBuff.append(folderName); + if (tokenizer.hasMoreTokens()) + { + pathBuff.append('/'); + } } + this.folderPath = pathBuff.toString(); } /** @@ -133,24 +120,26 @@ public class RepositoryFolderConfigBean extends RepositoryPathConfigBean " Base path: " + getRootPath()); } // Just choose the root path if the folder path is empty - if (folderPath.size() == 0) + if (folderPath.length() == 0) { return pathStartNodeRef; } else { - try + List nodeRefs = searchService.selectNodes(pathStartNodeRef, folderPath, null, namespaceService, true); + if (nodeRefs.size() == 0) { - FileInfo folderInfo = fileFolderService.resolveNamePath(pathStartNodeRef, folderPath); + throw new AlfrescoRuntimeException("Folder not found: " + this); + } + else + { + NodeRef nodeRef = nodeRefs.get(0); + FileInfo folderInfo = fileFolderService.getFileInfo(nodeRef); if (!folderInfo.isFolder()) { throw new AlfrescoRuntimeException("Not a folder: " + this); } - return folderInfo.getNodeRef(); - } - catch (FileNotFoundException e) - { - throw new AlfrescoRuntimeException("Folder not found: " + this); + return nodeRef; } } // Done @@ -179,18 +168,28 @@ public class RepositoryFolderConfigBean extends RepositoryPathConfigBean " Base path: " + getRootPath()); } // Just choose the root path if the folder path is empty - if (folderPath.size() == 0) + if (folderPath.length() == 0) { return pathStartNodeRef; } else { - FileInfo folderInfo = FileFolderServiceImpl.makeFolders( - fileFolderService, - pathStartNodeRef, - folderPath, - ContentModel.TYPE_FOLDER); - return folderInfo.getNodeRef(); + StringTokenizer folders = new StringTokenizer(folderPath, "/"); + NodeRef nodeRef = pathStartNodeRef; + while (folders.hasMoreTokens()) + { + QName folderQName = QName.createQName(folders.nextToken(), namespaceService); + List children = nodeService.getChildAssocs(nodeRef, RegexQNamePattern.MATCH_ALL, folderQName); + if (children.isEmpty()) + { + nodeRef = fileFolderService.create(nodeRef, folderQName.getLocalName(), ContentModel.TYPE_FOLDER, folderQName).getNodeRef(); + } + else + { + nodeRef = children.get(0).getChildRef(); + } + } + return nodeRef; } // Done }