mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged V3.4-BUG-FIX to HEAD
29716: ALF-4029: FileContentStore does not remove empty folders when deleting URLs delete() now crawls back up the dir hierarchy deleting directories until a non-empty parent is found. 29767: ALF-9351 No exception with invalid permission definitions - DTD/Schema validation added - Corrected permissionDefinitions.xml (contained extra -->) so failed validation 29797: ALF-9916 Audit user actions for site (added st:site to list of types in filter) 29800: ALF-5499 IndexOutOfBoundsException in QuickSort Need to synchronize access to JSF session beans. Added a filter that will use the HttpSession (if it exists) as the monitor for a synchronized block so that only one request per session is processed at any time. Approach taken in preference to adding synchronized blocks/methods or locks to over 200 session beans in the Alfresco Explorer UI. 29801: ALF-9190: If a user is invited to a site but joins the site independently, they end up with the "Consumer" role, regardless of the role they were invited with 29805: ALF-4029: added utility to make some rough timings. 29819: Merged DEV/TEMPORARY to V3.4-BUG-FIX (reviewed by Erik) 29815: ALF-8414 : Remove button does not show on Flash upload in Share Changed flash-upload.css styles to correct upload dialog display in IE6/IE7. Changes were tested in all supported browsers. 29826: Fix for ALF-9930 29836: Merged V3.4-TEAM to V3.4-BUG-FIX 27772: Incorrect behavior of enabling Google docs (Really: Forms get submitted twice in certain circumstances) 29839: ALF-9351 No exception with invalid permission definitions - Added permissionsDefinitions.xml to RM's permissionsModelDAO spring bean def (root cause of build errors) - Use UTF-8 encoding rather than server default when writing out modified form of model xml - Use byte[] rather than a temp file (model files should be small) - Added and then commented out an approach that did not require the need to write out the model xml (works for schema but not dtd. See notes in javax.xml.validation.SchemaFactory) - Fixed problem with RM recordsPermissionModel.xml (select -> selected) 29841: Fixed ALF-9826 "Folder is copied without content even if 'Apply rule to subfolders' check-box is checked" - Added "deep-copy" option for Copy action git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29863 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -406,49 +406,49 @@
|
||||
<!-- Permissions specific to avm website folder -->
|
||||
<!-- ========================================== -->
|
||||
|
||||
<permissionSet type="wcm:avmfolder" expose="selected"> -->
|
||||
<permissionSet type="wcm:avmfolder" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentReviewer" extends="true" expose="true" />
|
||||
</permissionSet>
|
||||
|
||||
<permissionSet type="wcm:avmplainfolder" expose="selected"> -->
|
||||
<permissionSet type="wcm:avmplainfolder" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentReviewer" extends="true" expose="true" />
|
||||
</permissionSet>
|
||||
|
||||
<permissionSet type="wcm:avmlayeredfolder" expose="selected"> -->
|
||||
<permissionSet type="wcm:avmlayeredfolder" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentReviewer" extends="true" expose="true" />
|
||||
</permissionSet>
|
||||
|
||||
<permissionSet type="wcm:avmcontent" expose="selected"> -->
|
||||
<permissionSet type="wcm:avmcontent" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentReviewer" extends="true" expose="true" />
|
||||
</permissionSet>
|
||||
|
||||
<permissionSet type="wcm:avmplaincontent" expose="selected"> -->
|
||||
<permissionSet type="wcm:avmplaincontent" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentReviewer" extends="true" expose="true" />
|
||||
</permissionSet>
|
||||
|
||||
<permissionSet type="wcm:avmlayeredcontent" expose="selected"> -->
|
||||
<permissionSet type="wcm:avmlayeredcontent" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentReviewer" extends="true" expose="true" />
|
||||
</permissionSet>
|
||||
|
||||
<permissionSet type="wca:webfolder" expose="selected"> -->
|
||||
<permissionSet type="wca:webfolder" expose="selected">
|
||||
<permissionGroup name="ContentManager" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentPublisher" extends="true" expose="true" />
|
||||
<permissionGroup name="ContentContributor" extends="true" expose="true" />
|
||||
|
@@ -22,6 +22,9 @@
|
||||
<property name="model">
|
||||
<value>alfresco/model/permissionDefinitions.xml</value>
|
||||
</property>
|
||||
<property name="dtdSchema">
|
||||
<value>alfresco/model/permissionSchema.dtd</value>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService" />
|
||||
</property>
|
||||
|
@@ -350,7 +350,7 @@ audit.config.strict=false
|
||||
# Audit map filter for AccessAuditor - restricts recorded events to user driven events
|
||||
audit.filter.alfresco-access.default.enabled=true
|
||||
audit.filter.alfresco-access.transaction.user=~System;~null;.*
|
||||
audit.filter.alfresco-access.transaction.type=cm:folder;cm:content
|
||||
audit.filter.alfresco-access.transaction.type=cm:folder;cm:content;st:site
|
||||
audit.filter.alfresco-access.transaction.path=~/sys:archivedItem;~/ver:;.*
|
||||
|
||||
|
||||
|
@@ -79,13 +79,13 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
*
|
||||
* The following properties are set by default to discard events where the user is
|
||||
* 'null' or 'System', the node path is '/sys:archivedItem' or under '/ver:' or
|
||||
* the node type is not 'cm:folder' or 'cm:content'. These values result in events
|
||||
* only being recorded if they are initiated by users of the system. These vales may
|
||||
* be overridden if required.
|
||||
* the node type is not 'cm:folder', 'cm:content' or 'st:site'. These values result
|
||||
* in events only being recorded for common actions initiated by users of the system.
|
||||
* These vales may be overridden if required.
|
||||
* <pre>
|
||||
* audit.filter.alfresco-access.default.enabled=true
|
||||
* audit.filter.alfresco-access.transaction.user=~System;~null;.*
|
||||
* audit.filter.alfresco-access.transaction.type=cm:folder;cm:content
|
||||
* audit.filter.alfresco-access.transaction.type=cm:folder;cm:content;st:site
|
||||
* audit.filter.alfresco-access.transaction.path=~/sys:archivedItem;~/ver:;.*
|
||||
* </pre>
|
||||
*
|
||||
|
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.alfresco.repo.content.filestore;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.content.cleanup.ContentStoreCleaner;
|
||||
import org.alfresco.repo.content.cleanup.ContentStoreCleanerListener;
|
||||
import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner;
|
||||
import org.alfresco.repo.domain.avm.AVMNodeDAO;
|
||||
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
|
||||
import org.alfresco.repo.lock.JobLockService;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
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.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
public class DeletionMetricsRunner
|
||||
{
|
||||
private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
private ContentService contentService;
|
||||
private NodeService nodeService;
|
||||
private TransactionService transactionService;
|
||||
private JobLockService jobLockService;
|
||||
private ContentStoreCleaner cleaner;
|
||||
private EagerContentStoreCleaner eagerCleaner;
|
||||
private FileContentStore store;
|
||||
private ContentStoreCleanerListener listener;
|
||||
private int deletedUrls;
|
||||
|
||||
private final int numOrphans = 1000;
|
||||
|
||||
|
||||
|
||||
public DeletionMetricsRunner()
|
||||
{
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
|
||||
contentService = serviceRegistry.getContentService();
|
||||
nodeService = serviceRegistry.getNodeService();
|
||||
transactionService = serviceRegistry.getTransactionService();
|
||||
jobLockService = serviceRegistry.getJobLockService();
|
||||
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||
DictionaryService dictionaryService = serviceRegistry.getDictionaryService();
|
||||
AVMNodeDAO avmNodeDAO = (AVMNodeDAO) ctx.getBean("newAvmNodeDAO");
|
||||
ContentDataDAO contentDataDAO = (ContentDataDAO) ctx.getBean("contentDataDAO");
|
||||
|
||||
// we need a store
|
||||
store = (FileContentStore) ctx.getBean("fileContentStore");
|
||||
|
||||
|
||||
// and a listener
|
||||
List<ContentStoreCleanerListener> listeners = new ArrayList<ContentStoreCleanerListener>(2);
|
||||
listener = new CleanerListener();
|
||||
listeners.add(listener);
|
||||
|
||||
// Construct the test cleaners
|
||||
eagerCleaner = (EagerContentStoreCleaner) ctx.getBean("eagerContentStoreCleaner");
|
||||
eagerCleaner.setEagerOrphanCleanup(false);
|
||||
eagerCleaner.setStores(Collections.singletonList((ContentStore) store));
|
||||
eagerCleaner.setListeners(listeners);
|
||||
|
||||
cleaner = new ContentStoreCleaner();
|
||||
cleaner.setEagerContentStoreCleaner(eagerCleaner);
|
||||
cleaner.setJobLockService(jobLockService);
|
||||
cleaner.setContentDataDAO(contentDataDAO);
|
||||
cleaner.setTransactionService(transactionService);
|
||||
cleaner.setDictionaryService(dictionaryService);
|
||||
cleaner.setContentService(contentService);
|
||||
cleaner.setAvmNodeDAO(avmNodeDAO);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
DeletionMetricsRunner metrics = new DeletionMetricsRunner();
|
||||
metrics.run();
|
||||
}
|
||||
|
||||
|
||||
public void run()
|
||||
{
|
||||
setUp(true);
|
||||
time("Deleting empty parent dirs");
|
||||
tearDown();
|
||||
|
||||
setUp(false);
|
||||
time("Ignoring empty parent dirs");
|
||||
tearDown();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void setUp(boolean deleteEmptyDirs)
|
||||
{
|
||||
AuthenticationUtil.setRunAsUserSystem();
|
||||
store.setDeleteEmptyDirs(deleteEmptyDirs);
|
||||
deletedUrls = 0;
|
||||
}
|
||||
|
||||
|
||||
private void tearDown()
|
||||
{
|
||||
AuthenticationUtil.clearCurrentSecurityContext();
|
||||
System.out.println("Deleted " + deletedUrls + " URLs.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void time(String description)
|
||||
{
|
||||
long beforeClean = System.currentTimeMillis();
|
||||
|
||||
createContent();
|
||||
cleanContent();
|
||||
|
||||
long afterClean = System.currentTimeMillis();
|
||||
double timeTaken = afterClean - beforeClean;
|
||||
System.out.println();
|
||||
System.out.println(String.format("%s took %6.0fms", description, timeTaken));
|
||||
}
|
||||
|
||||
|
||||
private void createContent()
|
||||
{
|
||||
final StoreRef storeRef = nodeService.createStore("test", "timings-" + GUID.generate());
|
||||
RetryingTransactionCallback<ContentData> testCallback = new RetryingTransactionCallback<ContentData>()
|
||||
{
|
||||
public ContentData execute() throws Throwable
|
||||
{
|
||||
ContentData contentData = null;
|
||||
|
||||
for (int i = 0; i < numOrphans; i++)
|
||||
{
|
||||
// Create some content
|
||||
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
|
||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(13);
|
||||
properties.put(ContentModel.PROP_NAME, (Serializable)"test.txt");
|
||||
|
||||
NodeRef contentNodeRef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ContentModel.ASSOC_CHILDREN,
|
||||
ContentModel.ASSOC_CHILDREN,
|
||||
ContentModel.TYPE_CONTENT,
|
||||
properties).getChildRef();
|
||||
|
||||
ContentWriter writer = contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
|
||||
|
||||
|
||||
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
|
||||
writer.putContent("INITIAL CONTENT");
|
||||
|
||||
contentData = writer.getContentData();
|
||||
|
||||
// Delete the first node, bypassing archive
|
||||
nodeService.addAspect(contentNodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||
nodeService.deleteNode(contentNodeRef);
|
||||
}
|
||||
|
||||
// Done
|
||||
return contentData;
|
||||
}
|
||||
};
|
||||
transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
|
||||
}
|
||||
|
||||
|
||||
private void cleanContent()
|
||||
{
|
||||
// fire the cleaner
|
||||
cleaner.setProtectDays(0);
|
||||
cleaner.execute();
|
||||
|
||||
if (deletedUrls < numOrphans)
|
||||
throw new IllegalStateException("Not all the orphans were cleaned.");
|
||||
}
|
||||
|
||||
|
||||
private class CleanerListener implements ContentStoreCleanerListener
|
||||
{
|
||||
public void beforeDelete(ContentStore store, String contentUrl) throws ContentIOException
|
||||
{
|
||||
deletedUrls++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -71,6 +71,7 @@ public class FileContentStore
|
||||
private boolean allowRandomAccess;
|
||||
private boolean readOnly;
|
||||
private ApplicationContext applicationContext;
|
||||
private boolean deleteEmptyDirs = true;
|
||||
|
||||
/**
|
||||
* Private: for Spring-constructed instances only.
|
||||
@@ -609,6 +610,12 @@ public class FileContentStore
|
||||
deleted = file.delete();
|
||||
}
|
||||
|
||||
// Delete empty parents regardless of whether the file was ignore above.
|
||||
if (deleteEmptyDirs && deleted)
|
||||
{
|
||||
deleteEmptyParents(file);
|
||||
}
|
||||
|
||||
// done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
@@ -619,6 +626,39 @@ public class FileContentStore
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the parents of the specified file. The file itself must have been
|
||||
* deleted before calling this method - since only empty directories can be deleted.
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
private void deleteEmptyParents(File file)
|
||||
{
|
||||
String root = getRootLocation();
|
||||
File parent = file.getParentFile();
|
||||
boolean deleted = false;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
if (parent.isDirectory() && !parent.getCanonicalPath().equals(root))
|
||||
{
|
||||
// Only an empty directory will successfully be deleted.
|
||||
deleted = parent.delete();
|
||||
}
|
||||
}
|
||||
catch (IOException error)
|
||||
{
|
||||
logger.error("Unable to construct canonical path for " + parent.getAbsolutePath());
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent.getParentFile();
|
||||
}
|
||||
while(deleted);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new content URL. This must be supported by all
|
||||
* stores that are compatible with Alfresco.
|
||||
@@ -669,4 +709,14 @@ public class FileContentStore
|
||||
publishEvent(((ContextRefreshedEvent) event).getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the FileContentStore to delete empty parent directories upon deleting a content URL.
|
||||
*
|
||||
* @param deleteEmptyDirs the deleteEmptyDirs to set
|
||||
*/
|
||||
public void setDeleteEmptyDirs(boolean deleteEmptyDirs)
|
||||
{
|
||||
this.deleteEmptyDirs = deleteEmptyDirs;
|
||||
}
|
||||
}
|
||||
|
@@ -50,6 +50,8 @@ public class FileContentStoreTest extends AbstractWritableContentStoreTest
|
||||
tempDir.getAbsolutePath() +
|
||||
File.separatorChar +
|
||||
getName());
|
||||
|
||||
store.setDeleteEmptyDirs(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,4 +130,101 @@ public class FileContentStoreTest extends AbstractWritableContentStoreTest
|
||||
assertTrue("Size must be positive", size > 0L);
|
||||
assertTrue("Size must not be Long.MAX_VALUE", size < Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Empty parent directories should be removed when a URL is removed.
|
||||
*/
|
||||
public void testDeleteRemovesEmptyDirs() throws Exception
|
||||
{
|
||||
ContentStore store = getStore();
|
||||
String url = "store://1965/12/1/13/12/file.bin";
|
||||
|
||||
// Ensure clean test data
|
||||
if (store.exists(url)) store.delete(url);
|
||||
|
||||
String content = "Content for test: " + getName();
|
||||
store.getWriter(new ContentContext(null, url)).putContent(content);
|
||||
|
||||
File root = new File(store.getRootLocation());
|
||||
|
||||
assertDirExists(root, "");
|
||||
assertDirExists(root, "1965/12/1/13/12");
|
||||
|
||||
store.delete(url);
|
||||
|
||||
assertDirNotExists(root, "1965");
|
||||
// root should be untouched.
|
||||
assertDirExists(root, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Only non-empty directories should be deleted.
|
||||
*/
|
||||
public void testDeleteLeavesNonEmptyDirs()
|
||||
{
|
||||
ContentStore store = getStore();
|
||||
String url = "store://1965/12/1/13/12/file.bin";
|
||||
|
||||
// Ensure clean test data
|
||||
if (store.exists(url)) store.delete(url);
|
||||
|
||||
String content = "Content for test: " + getName();
|
||||
store.getWriter(new ContentContext(null, url)).putContent(content);
|
||||
|
||||
File root = new File(store.getRootLocation());
|
||||
|
||||
assertDirExists(root, "");
|
||||
assertDirExists(root, "1965/12/1/13/12");
|
||||
|
||||
// Make a directory non-empty
|
||||
String anotherUrl = "store://1965/12/3/another.bin";
|
||||
if (store.exists(anotherUrl)) store.delete(anotherUrl);
|
||||
store.getWriter(new ContentContext(null, anotherUrl));
|
||||
|
||||
store.delete(url);
|
||||
|
||||
// Parents of another.bin cannot be deleted
|
||||
assertDirExists(root, "1965");
|
||||
assertDirExists(root, "1965/12");
|
||||
// Non-parents of another.bin could be deleted
|
||||
assertDirNotExists(root, "1965/12/1");
|
||||
|
||||
// root should be untouched.
|
||||
assertDirExists(root, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Empty parent directories are not deleted if the store is configured not to.
|
||||
*/
|
||||
public void testNoParentDirsDeleted() throws Exception
|
||||
{
|
||||
store.setDeleteEmptyDirs(false);
|
||||
FileContentStore store = (FileContentStore) getStore();
|
||||
String url = "store://1965/12/1/13/12/file.bin";
|
||||
// Ensure clean test data
|
||||
if (store.exists(url)) store.delete(url);
|
||||
String content = "Content for test: " + getName();
|
||||
store.getWriter(new ContentContext(null, url)).putContent(content);
|
||||
File root = new File(store.getRootLocation());
|
||||
|
||||
store.delete(url);
|
||||
|
||||
assertDirExists(root, "1965/12/1/13/12");
|
||||
// root should be untouched.
|
||||
assertDirExists(root, "");
|
||||
}
|
||||
|
||||
|
||||
private void assertDirExists(File root, String dir)
|
||||
{
|
||||
assertTrue("Directory [" + dir + "] should exist", new File(root, dir).exists());
|
||||
}
|
||||
|
||||
|
||||
private void assertDirNotExists(File root, String dir)
|
||||
{
|
||||
assertFalse("Directory [" + dir + "] should NOT exist", new File(root, dir).exists());
|
||||
}
|
||||
}
|
||||
|
@@ -70,8 +70,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import com.aetrion.flickr.auth.AuthUtilities;
|
||||
|
||||
/**
|
||||
* Helper class to house utility methods common to
|
||||
* more than one Invite Service Web Script
|
||||
@@ -145,7 +143,7 @@ public class InviteHelper implements InitializingBean
|
||||
return null;
|
||||
}
|
||||
});
|
||||
addSiteMembership(invitee, siteShortName, role, inviter);
|
||||
addSiteMembership(invitee, siteShortName, role, inviter, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,14 +266,18 @@ public class InviteHelper implements InitializingBean
|
||||
* @param role
|
||||
* @param runAsUser
|
||||
* @param siteService
|
||||
* @param overrideExisting
|
||||
*/
|
||||
public void addSiteMembership(final String invitee, final String siteName, final String role, final String runAsUser)
|
||||
public void addSiteMembership(final String invitee, final String siteName, final String role, final String runAsUser, final boolean overrideExisting)
|
||||
{
|
||||
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||
{
|
||||
public Void doWork() throws Exception
|
||||
{
|
||||
if (overrideExisting || !siteService.isMember(siteName, invitee))
|
||||
{
|
||||
siteService.setMembership(siteName, invitee, role);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -415,7 +417,7 @@ public class InviteHelper implements InitializingBean
|
||||
String reviewer = (String)executionVariables.get(WorkflowModelModeratedInvitation.wfVarReviewer);
|
||||
|
||||
// Add invitee to the site
|
||||
addSiteMembership(invitee, siteName, role, reviewer);
|
||||
addSiteMembership(invitee, siteName, role, reviewer, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@@ -18,8 +18,11 @@
|
||||
*/
|
||||
package org.alfresco.repo.security.permissions.impl.model;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
@@ -36,8 +39,8 @@ import org.alfresco.repo.security.permissions.PermissionEntry;
|
||||
import org.alfresco.repo.security.permissions.PermissionReference;
|
||||
import org.alfresco.repo.security.permissions.impl.ModelDAO;
|
||||
import org.alfresco.repo.security.permissions.impl.RequiredPermission;
|
||||
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
|
||||
import org.alfresco.repo.security.permissions.impl.RequiredPermission.On;
|
||||
import org.alfresco.repo.security.permissions.impl.SimplePermissionReference;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
@@ -53,8 +56,12 @@ import org.alfresco.util.Pair;
|
||||
import org.dom4j.Attribute;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentType;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import org.dom4j.tree.DefaultDocumentType;
|
||||
|
||||
/**
|
||||
* The implementation of the model DAO Reads and stores the top level model information Encapsulates access to this
|
||||
@@ -93,6 +100,8 @@ public class PermissionModel implements ModelDAO
|
||||
// Instance variables
|
||||
|
||||
private String model;
|
||||
private String dtdSchema;
|
||||
private boolean validate = true;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -1142,6 +1151,26 @@ public class PermissionModel implements ModelDAO
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dtd schema that is used to validate permission model
|
||||
*
|
||||
* @param dtdSchema
|
||||
*/
|
||||
public void setDtdSchema(String dtdSchema)
|
||||
{
|
||||
this.dtdSchema = dtdSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether model should be validated on initialization against specified dtd
|
||||
*
|
||||
* @param validate
|
||||
*/
|
||||
public void setValidate(boolean validate)
|
||||
{
|
||||
this.validate = validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dictionary service
|
||||
*
|
||||
@@ -1261,6 +1290,9 @@ public class PermissionModel implements ModelDAO
|
||||
private Document createDocument(String model)
|
||||
{
|
||||
InputStream is = this.getClass().getClassLoader().getResourceAsStream(model);
|
||||
URL dtdSchemaUrl = (dtdSchema == null)
|
||||
? null
|
||||
: this.getClass().getClassLoader().getResource(dtdSchema);
|
||||
if (is == null)
|
||||
{
|
||||
throw new PermissionModelException("File not found: " + model);
|
||||
@@ -1268,19 +1300,154 @@ public class PermissionModel implements ModelDAO
|
||||
SAXReader reader = new SAXReader();
|
||||
try
|
||||
{
|
||||
if (validate)
|
||||
{
|
||||
if (dtdSchemaUrl != null)
|
||||
{
|
||||
is = processModelDocType(is, dtdSchemaUrl.toString());
|
||||
reader.setValidation(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PermissionModelException("Couldn't obtain DTD schema to validate permission model.");
|
||||
}
|
||||
}
|
||||
|
||||
Document document = reader.read(is);
|
||||
is.close();
|
||||
return document;
|
||||
}
|
||||
catch (DocumentException e)
|
||||
{
|
||||
throw new PermissionModelException("Failed to create permission model document ", e);
|
||||
throw new PermissionModelException("Failed to create permission model document: " + model, e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new PermissionModelException("Failed to close permission model document ", e);
|
||||
throw new PermissionModelException("Failed to close permission model document: " + model, e);
|
||||
}
|
||||
|
||||
// TODO Do something like the following so that we don't need to modify the source xml
|
||||
// to validate it. The following does not work for DTDs.
|
||||
|
||||
// InputStream is = this.getClass().getClassLoader().getResourceAsStream(model);
|
||||
// if (is == null)
|
||||
// {
|
||||
// throw new PermissionModelException("File not found: " + model);
|
||||
// }
|
||||
//
|
||||
// InputStream dtdSchemaIs = (dtdSchema == null)
|
||||
// ? null
|
||||
// : this.getClass().getClassLoader().getResourceAsStream(dtdSchema);
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// Document document;
|
||||
// SAXReader reader;
|
||||
// if (validate)
|
||||
// {
|
||||
// if (dtdSchemaIs != null)
|
||||
// {
|
||||
// SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
//
|
||||
// SchemaFactory schemaFactory =
|
||||
// SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// factory.setSchema(schemaFactory.newSchema(
|
||||
// new Source[] {new StreamSource(dtdSchemaIs)}));
|
||||
// SAXParser parser = factory.newSAXParser();
|
||||
// reader = new SAXReader(parser.getXMLReader());
|
||||
// reader.setValidation(false);
|
||||
// reader.setErrorHandler(new XMLErrorHandler());
|
||||
// }
|
||||
// catch (SAXException e)
|
||||
// {
|
||||
// throw new PermissionModelException("Failed to read DTD/schema: " + dtdSchema, e);
|
||||
// }
|
||||
// catch (ParserConfigurationException e)
|
||||
// {
|
||||
// throw new PermissionModelException("Failed to configure DTD/schema: " + dtdSchema, e);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// throw new PermissionModelException("Couldn't obtain DTD/schema to validate permission model.");
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// reader = new SAXReader();
|
||||
// }
|
||||
// document = reader.read(is);
|
||||
// return document;
|
||||
// }
|
||||
// catch (DocumentException e)
|
||||
// {
|
||||
// throw new PermissionModelException("Failed to create permission model document: " + model, e);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// if (is != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// is.close();
|
||||
// }
|
||||
// catch (IOException e)
|
||||
// {
|
||||
// throw new PermissionModelException("Failed to close permission model document: " + model, e);
|
||||
// }
|
||||
// }
|
||||
// if (dtdSchemaIs != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// dtdSchemaIs.close();
|
||||
// }
|
||||
// catch (IOException e)
|
||||
// {
|
||||
// throw new PermissionModelException("Couldn't close DTD/schema to validate permission model.");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace or add correct DOCTYPE to the xml to allow validation against dtd
|
||||
*/
|
||||
private InputStream processModelDocType(InputStream is, String dtdSchemaUrl) throws DocumentException, IOException
|
||||
{
|
||||
SAXReader reader = new SAXReader();
|
||||
// read document without validation
|
||||
Document doc = reader.read(is);
|
||||
DocumentType docType = doc.getDocType();
|
||||
if (docType != null)
|
||||
{
|
||||
// replace DOCTYPE setting the full path to the xsd
|
||||
docType.setSystemID(dtdSchemaUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the DOCTYPE
|
||||
docType = new DefaultDocumentType(doc.getRootElement().getName(), dtdSchemaUrl);
|
||||
doc.setDocType(docType);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream fos = new ByteArrayOutputStream();
|
||||
try
|
||||
{
|
||||
OutputFormat format = OutputFormat.createPrettyPrint(); // uses UTF-8
|
||||
XMLWriter writer = new XMLWriter(fos, format);
|
||||
writer.write(doc);
|
||||
writer.flush();
|
||||
}
|
||||
finally
|
||||
{
|
||||
fos.close();
|
||||
}
|
||||
|
||||
return new ByteArrayInputStream(fos.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user