NXP-3638: add BulkEditActions Seam bean to handle bulk edit on a set of Documents
1.1 --- a/nuxeo-platform-webapp-base/pom.xml
1.2 +++ b/nuxeo-platform-webapp-base/pom.xml
1.3 @@ -263,6 +263,16 @@
1.4 <artifactId>nuxeo-core-jcr-connector-test</artifactId>
1.5 <scope>test</scope>
1.6 </dependency>
1.7 + <dependency>
1.8 + <groupId>org.nuxeo.ecm.core</groupId>
1.9 + <artifactId>nuxeo-core-test</artifactId>
1.10 + <scope>test</scope>
1.11 + </dependency>
1.12 + <dependency>
1.13 + <groupId>org.nuxeo.ecm.platform</groupId>
1.14 + <artifactId>nuxeo-platform-types-core</artifactId>
1.15 + <scope>test</scope>
1.16 + </dependency>
1.17 </dependencies>
1.18
1.19 <build>
2.1 new file mode 100644
2.2 --- /dev/null
2.3 +++ b/nuxeo-platform-webapp-base/src/main/java/org/nuxeo/ecm/webapp/bulkedit/BulkEditActions.java
2.4 @@ -0,0 +1,116 @@
2.5 +/*
2.6 + * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
2.7 + *
2.8 + * All rights reserved. This program and the accompanying materials
2.9 + * are made available under the terms of the GNU Lesser General Public License
2.10 + * (LGPL) version 2.1 which accompanies this distribution, and is available at
2.11 + * http://www.gnu.org/licenses/lgpl.html
2.12 + *
2.13 + * This library is distributed in the hope that it will be useful,
2.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2.16 + * Lesser General Public License for more details.
2.17 + *
2.18 + * Contributors:
2.19 + * Thomas Roger
2.20 + */
2.21 +
2.22 +package org.nuxeo.ecm.webapp.bulkedit;
2.23 +
2.24 +import java.io.Serializable;
2.25 +import java.util.Collections;
2.26 +import java.util.List;
2.27 +
2.28 +import org.jboss.seam.ScopeType;
2.29 +import org.jboss.seam.annotations.Factory;
2.30 +import org.jboss.seam.annotations.In;
2.31 +import org.jboss.seam.annotations.Install;
2.32 +import org.jboss.seam.annotations.Name;
2.33 +import org.jboss.seam.annotations.Scope;
2.34 +import org.nuxeo.ecm.core.api.ClientException;
2.35 +import org.nuxeo.ecm.core.api.CoreSession;
2.36 +import org.nuxeo.ecm.core.api.DocumentModel;
2.37 +import org.nuxeo.ecm.platform.types.TypeManager;
2.38 +import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
2.39 +import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
2.40 +
2.41 +import static org.jboss.seam.ScopeType.CONVERSATION;
2.42 +
2.43 +/**
2.44 + * Handles Bulk Edit actions.
2.45 + *
2.46 + * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
2.47 + */
2.48 +@Name("bulkEditActions")
2.49 +@Scope(CONVERSATION)
2.50 +@Install(precedence = Install.FRAMEWORK)
2.51 +public class BulkEditActions implements Serializable {
2.52 +
2.53 + private static final long serialVersionUID = 1L;
2.54 +
2.55 + @In(create = true)
2.56 + protected transient DocumentsListsManager documentsListsManager;
2.57 +
2.58 + @In(create = true)
2.59 + protected transient TypeManager typeManager;
2.60 +
2.61 + @In(create = true)
2.62 + protected transient CoreSession documentManager;
2.63 +
2.64 + @In(create = true)
2.65 + protected transient NavigationContext navigationContext;
2.66 +
2.67 + protected DocumentModel fictiveDocumentModel;
2.68 +
2.69 + /**
2.70 + * Returns the common layouts of the current selected documents for the
2.71 + * {@code edit} mode.
2.72 + */
2.73 + public List<String> getCommonsLayouts() {
2.74 + if (documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) {
2.75 + return Collections.emptyList();
2.76 + }
2.77 +
2.78 + List<DocumentModel> selectedDocuments = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
2.79 + return BulkEditHelper.getCommonLayouts(typeManager, selectedDocuments);
2.80 + }
2.81 +
2.82 + /**
2.83 + * Returns the common schemas for the current selected documents.
2.84 + */
2.85 + protected List<String> getCommonSchemas() {
2.86 + if (documentsListsManager.isWorkingListEmpty(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION)) {
2.87 + return Collections.emptyList();
2.88 + }
2.89 +
2.90 + List<DocumentModel> selectedDocuments = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
2.91 + return BulkEditHelper.getCommonSchemas(selectedDocuments);
2.92 + }
2.93 +
2.94 + @Factory(value = "bulkEditDocumentModel", scope = ScopeType.EVENT)
2.95 + public DocumentModel getBulkEditDocumentModel() {
2.96 + if (fictiveDocumentModel == null) {
2.97 + fictiveDocumentModel = FictiveDocumentModel.createFictiveDocumentModelWith(getCommonSchemas());
2.98 + }
2.99 + return fictiveDocumentModel;
2.100 + }
2.101 +
2.102 + public String bulkEditSelection() throws ClientException {
2.103 + bulkEditSelectionNoRedirect();
2.104 + return navigationContext.navigateToDocument(navigationContext.getCurrentDocument());
2.105 + }
2.106 +
2.107 + public void bulkEditSelectionNoRedirect() throws ClientException {
2.108 + if (fictiveDocumentModel != null) {
2.109 + List<DocumentModel> selectedDocuments = documentsListsManager.getWorkingList(DocumentsListsManager.CURRENT_DOCUMENT_SELECTION);
2.110 + BulkEditHelper.copyMetadata(documentManager, fictiveDocumentModel,
2.111 + selectedDocuments);
2.112 + fictiveDocumentModel = null;
2.113 + }
2.114 + }
2.115 +
2.116 + public void cancel() {
2.117 + fictiveDocumentModel = null;
2.118 + }
2.119 +
2.120 +}
3.1 new file mode 100644
3.2 --- /dev/null
3.3 +++ b/nuxeo-platform-webapp-base/src/main/java/org/nuxeo/ecm/webapp/bulkedit/BulkEditHelper.java
3.4 @@ -0,0 +1,136 @@
3.5 +/*
3.6 + * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
3.7 + *
3.8 + * All rights reserved. This program and the accompanying materials
3.9 + * are made available under the terms of the GNU Lesser General Public License
3.10 + * (LGPL) version 2.1 which accompanies this distribution, and is available at
3.11 + * http://www.gnu.org/licenses/lgpl.html
3.12 + *
3.13 + * This library is distributed in the hope that it will be useful,
3.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3.16 + * Lesser General Public License for more details.
3.17 + *
3.18 + * Contributors:
3.19 + * Thomas Roger
3.20 + */
3.21 +
3.22 +package org.nuxeo.ecm.webapp.bulkedit;
3.23 +
3.24 +import java.io.Serializable;
3.25 +import java.util.ArrayList;
3.26 +import java.util.Arrays;
3.27 +import java.util.List;
3.28 +import java.util.Map;
3.29 +
3.30 +import org.nuxeo.ecm.core.api.ClientException;
3.31 +import org.nuxeo.ecm.core.api.CoreSession;
3.32 +import org.nuxeo.ecm.core.api.DocumentModel;
3.33 +import org.nuxeo.ecm.platform.forms.layout.api.BuiltinModes;
3.34 +import org.nuxeo.ecm.platform.types.Type;
3.35 +import org.nuxeo.ecm.platform.types.TypeManager;
3.36 +
3.37 +/**
3.38 + * Helper used for bulk edit actions
3.39 + *
3.40 + * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
3.41 + */
3.42 +public class BulkEditHelper {
3.43 +
3.44 + public static final String BULK_EDIT_PREFIX = "bulkEdit/";
3.45 +
3.46 + private BulkEditHelper() {
3.47 + // Helper class
3.48 + }
3.49 +
3.50 + /**
3.51 + * Returns the common layouts of the {@code docs} for the {@code edit} mode.
3.52 + */
3.53 + public static List<String> getCommonLayouts(TypeManager typeManager,
3.54 + List<DocumentModel> docs) {
3.55 + return getCommonLayouts(typeManager, docs, BuiltinModes.EDIT);
3.56 + }
3.57 +
3.58 + /**
3.59 + * Returns the common layouts of the {@code docs} for the given layout
3.60 + * {@code mode}.
3.61 + */
3.62 + public static List<String> getCommonLayouts(TypeManager typeManager,
3.63 + List<DocumentModel> docs, String mode) {
3.64 + List<String> layouts = null;
3.65 + for (DocumentModel doc : docs) {
3.66 + Type type = typeManager.getType(doc.getType());
3.67 + List<String> typeLayouts = Arrays.asList(type.getLayouts(mode));
3.68 + if (layouts == null) {
3.69 + // first document
3.70 + layouts = new ArrayList<String>();
3.71 + layouts.addAll(typeLayouts);
3.72 + } else {
3.73 + layouts.retainAll(typeLayouts);
3.74 + }
3.75 + }
3.76 + return layouts;
3.77 + }
3.78 +
3.79 + /**
3.80 + * Returns the common schemas of the {@code docs}.
3.81 + */
3.82 + public static List<String> getCommonSchemas(List<DocumentModel> docs) {
3.83 + List<String> schemas = null;
3.84 + for (DocumentModel doc : docs) {
3.85 + List<String> docSchemas = Arrays.asList(doc.getDeclaredSchemas());
3.86 + if (schemas == null) {
3.87 + // first document
3.88 + schemas = new ArrayList<String>();
3.89 + schemas.addAll(docSchemas);
3.90 + } else {
3.91 + schemas.retainAll(docSchemas);
3.92 + }
3.93 + }
3.94 + return schemas;
3.95 + }
3.96 +
3.97 + /**
3.98 + * Copy all the marked properties (stored in the ContextData of {@code
3.99 + * sourceDoc}) from {@code sourceDoc} to all the {@code targetDocs}.
3.100 + *
3.101 + * @param session the {@code CoreSession} to use
3.102 + * @param sourceDoc the doc where to get the metadata to copy
3.103 + * @param targetDocs the docs where to set the metadatas
3.104 + */
3.105 + public static void copyMetadata(CoreSession session,
3.106 + DocumentModel sourceDoc, List<DocumentModel> targetDocs)
3.107 + throws ClientException {
3.108 + List<String> propertiesToCopy = getPropertiesToCopy(sourceDoc);
3.109 + for (DocumentModel targetDoc : targetDocs) {
3.110 + for (String propertyToCopy : propertiesToCopy) {
3.111 + targetDoc.setPropertyValue(propertyToCopy,
3.112 + sourceDoc.getPropertyValue(propertyToCopy));
3.113 + }
3.114 + }
3.115 + session.saveDocuments(targetDocs.toArray(new DocumentModel[targetDocs.size()]));
3.116 + session.save();
3.117 + }
3.118 +
3.119 + /**
3.120 + * Extracts the properties to be copied from {@code sourceDoc}. The
3.121 + * properties are stored in the ContextData of {@code sourceDoc}: the key
3.122 + * is the xpath property, the value is {@code true} if the property has to
3.123 + * be copied, {@code false otherwise}.
3.124 + */
3.125 + protected static List<String> getPropertiesToCopy(DocumentModel sourceDoc) {
3.126 + List<String> propertiesToCopy = new ArrayList<String>();
3.127 + for (Map.Entry<String, Serializable> entry : sourceDoc.getContextData().entrySet()) {
3.128 + String key = entry.getKey();
3.129 + if (key.startsWith(BULK_EDIT_PREFIX)) {
3.130 + String[] properties = key.replace(BULK_EDIT_PREFIX, "").split(" ");
3.131 + Serializable value = entry.getValue();
3.132 + if (value instanceof Boolean && (Boolean) value) {
3.133 + propertiesToCopy.addAll(Arrays.asList(properties));
3.134 + }
3.135 + }
3.136 + }
3.137 + return propertiesToCopy;
3.138 + }
3.139 +
3.140 +}
4.1 new file mode 100644
4.2 --- /dev/null
4.3 +++ b/nuxeo-platform-webapp-base/src/main/java/org/nuxeo/ecm/webapp/bulkedit/FictiveDataModel.java
4.4 @@ -0,0 +1,93 @@
4.5 +/*
4.6 + * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
4.7 + *
4.8 + * All rights reserved. This program and the accompanying materials
4.9 + * are made available under the terms of the GNU Lesser General Public License
4.10 + * (LGPL) version 2.1 which accompanies this distribution, and is available at
4.11 + * http://www.gnu.org/licenses/lgpl.html
4.12 + *
4.13 + * This library is distributed in the hope that it will be useful,
4.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4.16 + * Lesser General Public License for more details.
4.17 + *
4.18 + * Contributors:
4.19 + * Thomas Roger
4.20 + */
4.21 +
4.22 +package org.nuxeo.ecm.webapp.bulkedit;
4.23 +
4.24 +import java.util.Collection;
4.25 +import java.util.HashMap;
4.26 +import java.util.Map;
4.27 +
4.28 +import org.nuxeo.ecm.core.api.DataModel;
4.29 +import org.nuxeo.ecm.core.api.model.DocumentPart;
4.30 +import org.nuxeo.ecm.core.api.model.PropertyException;
4.31 +import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
4.32 +import org.nuxeo.ecm.core.api.model.impl.DefaultPropertyFactory;
4.33 +
4.34 +/**
4.35 + * A data model that is not tied to a particular schema, neither has anything to
4.36 + * do with a session (CoreSession).
4.37 + * <p>
4.38 + * Used just to hold data for a <code>FictiveDocumentModel</code> in a
4.39 + * non-constraining way.
4.40 + *
4.41 + * @author DM
4.42 + * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
4.43 + */
4.44 +public class FictiveDataModel implements DataModel {
4.45 +
4.46 + protected Map<String, Object> data = new HashMap<String, Object>();
4.47 +
4.48 + protected final DocumentPart dp;
4.49 +
4.50 + public FictiveDataModel(String schema) {
4.51 + dp = DefaultPropertyFactory.newDocumentPart(schema);
4.52 + }
4.53 +
4.54 + public void setData(String key, Object value) throws PropertyException {
4.55 + data.put(key, value);
4.56 + }
4.57 +
4.58 + public Object getData(String key) throws PropertyException {
4.59 + return data.get(key);
4.60 + }
4.61 +
4.62 + public String getSchema() {
4.63 + return dp.getSchema().getSchemaName();
4.64 + }
4.65 +
4.66 + public Map<String, Object> getMap() throws PropertyException {
4.67 + return data;
4.68 + }
4.69 +
4.70 + public void setMap(Map<String, Object> data) throws PropertyException {
4.71 + this.data = new HashMap<String, Object>(data);
4.72 + }
4.73 +
4.74 + public boolean isDirty() {
4.75 + return true;
4.76 + }
4.77 +
4.78 + public boolean isDirty(String name) throws PropertyNotFoundException {
4.79 + return true;
4.80 + }
4.81 +
4.82 + public void setDirty(String name) throws PropertyNotFoundException {
4.83 + }
4.84 +
4.85 + public Collection<String> getDirtyFields() {
4.86 + return data.keySet();
4.87 + }
4.88 +
4.89 + public Object getValue(String path) throws PropertyException {
4.90 + throw new UnsupportedOperationException();
4.91 + }
4.92 +
4.93 + public Object setValue(String path, Object value) throws PropertyException {
4.94 + throw new UnsupportedOperationException();
4.95 + }
4.96 +
4.97 +}
5.1 new file mode 100644
5.2 --- /dev/null
5.3 +++ b/nuxeo-platform-webapp-base/src/main/java/org/nuxeo/ecm/webapp/bulkedit/FictiveDocumentModel.java
5.4 @@ -0,0 +1,416 @@
5.5 +/*
5.6 + * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
5.7 + *
5.8 + * All rights reserved. This program and the accompanying materials
5.9 + * are made available under the terms of the GNU Lesser General Public License
5.10 + * (LGPL) version 2.1 which accompanies this distribution, and is available at
5.11 + * http://www.gnu.org/licenses/lgpl.html
5.12 + *
5.13 + * This library is distributed in the hope that it will be useful,
5.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5.16 + * Lesser General Public License for more details.
5.17 + *
5.18 + * Contributors:
5.19 + * Thomas Roger
5.20 + */
5.21 +
5.22 +package org.nuxeo.ecm.webapp.bulkedit;
5.23 +
5.24 +import java.io.Serializable;
5.25 +import java.util.Arrays;
5.26 +import java.util.Collection;
5.27 +import java.util.List;
5.28 +import java.util.Map;
5.29 +import java.util.Set;
5.30 +
5.31 +import org.nuxeo.common.collections.ScopeType;
5.32 +import org.nuxeo.common.collections.ScopedMap;
5.33 +import org.nuxeo.common.utils.Path;
5.34 +import org.nuxeo.ecm.core.api.ClientException;
5.35 +import org.nuxeo.ecm.core.api.CoreSession;
5.36 +import org.nuxeo.ecm.core.api.DataModel;
5.37 +import org.nuxeo.ecm.core.api.DataModelMap;
5.38 +import org.nuxeo.ecm.core.api.DocumentException;
5.39 +import org.nuxeo.ecm.core.api.DocumentModel;
5.40 +import org.nuxeo.ecm.core.api.DocumentRef;
5.41 +import org.nuxeo.ecm.core.api.impl.DataModelMapImpl;
5.42 +import org.nuxeo.ecm.core.api.model.DocumentPart;
5.43 +import org.nuxeo.ecm.core.api.model.Property;
5.44 +import org.nuxeo.ecm.core.api.model.PropertyException;
5.45 +import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
5.46 +import org.nuxeo.ecm.core.api.model.impl.DefaultPropertyFactory;
5.47 +import org.nuxeo.ecm.core.api.security.ACP;
5.48 +import org.nuxeo.ecm.core.schema.DocumentType;
5.49 +import org.nuxeo.ecm.core.schema.SchemaManager;
5.50 +import org.nuxeo.ecm.core.schema.types.Schema;
5.51 +import org.nuxeo.runtime.api.Framework;
5.52 +
5.53 +/**
5.54 + * A DocumentModel that can have any schema and is not made persistent by
5.55 + * itself. A mockup to keep arbitrary schema data.
5.56 + *
5.57 + * @author DM
5.58 + * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
5.59 + */
5.60 +public class FictiveDocumentModel implements DocumentModel {
5.61 +
5.62 + protected DataModelMap dataModels = new DataModelMapImpl();
5.63 +
5.64 + protected ScopedMap contextData = new ScopedMap();
5.65 +
5.66 + public static DocumentModel createFictiveDocumentModelWith(
5.67 + List<String> schemas) {
5.68 + FictiveDocumentModel doc = new FictiveDocumentModel();
5.69 + for (String schema : schemas) {
5.70 + DataModel dataModel = doc.dataModels.get(schema);
5.71 + if (dataModel == null) {
5.72 + dataModel = new FictiveDataModel(schema);
5.73 + doc.dataModels.put(schema, dataModel);
5.74 + }
5.75 + }
5.76 + return doc;
5.77 + }
5.78 +
5.79 + public static DocumentModel createFictiveDocumentModelWith(
5.80 + String... schemas) {
5.81 + return createFictiveDocumentModelWith(Arrays.asList(schemas));
5.82 + }
5.83 +
5.84 + protected FictiveDocumentModel() {
5.85 + }
5.86 +
5.87 + public String[] getDeclaredSchemas() {
5.88 + Set<String> keys = dataModels.keySet();
5.89 + return keys.toArray(new String[keys.size()]);
5.90 + }
5.91 +
5.92 + public Object getProperty(String schemaName, String name)
5.93 + throws ClientException {
5.94 + DataModel dm = dataModels.get(schemaName);
5.95 + return dm != null ? dm.getData(name) : null;
5.96 + }
5.97 +
5.98 + public void setProperty(String schemaName, String name, Object value)
5.99 + throws ClientException {
5.100 + if (name.contains(":")) {
5.101 + name = name.substring(name.indexOf(":"), name.length());
5.102 + }
5.103 + dataModels.get(schemaName).setData(name, value);
5.104 + }
5.105 +
5.106 + public Map<String, Object> getProperties(String schemaName)
5.107 + throws ClientException {
5.108 + return dataModels.get(schemaName).getMap();
5.109 + }
5.110 +
5.111 + public void setProperties(String schemaName, Map<String, Object> data)
5.112 + throws ClientException {
5.113 + dataModels.get(schemaName).setMap(data);
5.114 + }
5.115 +
5.116 + public ScopedMap getContextData() {
5.117 + return contextData;
5.118 + }
5.119 +
5.120 + public Serializable getContextData(ScopeType scope, String key) {
5.121 + return contextData.getScopedValue(scope, key);
5.122 + }
5.123 +
5.124 + public void putContextData(ScopeType scope, String key, Serializable value) {
5.125 + contextData.putScopedValue(scope, key, value);
5.126 + }
5.127 +
5.128 + public Serializable getContextData(String key) {
5.129 + return contextData.getScopedValue(key);
5.130 + }
5.131 +
5.132 + public void putContextData(String key, Serializable value) {
5.133 + contextData.putScopedValue(key, value);
5.134 + }
5.135 +
5.136 + public void copyContextData(DocumentModel otherDocument) {
5.137 + ScopedMap otherMap = otherDocument.getContextData();
5.138 + if (otherMap != null) {
5.139 + contextData.putAll(otherMap);
5.140 + }
5.141 + }
5.142 +
5.143 + public Property getProperty(String xpath) throws PropertyException,
5.144 + ClientException {
5.145 + Path path = new Path(xpath);
5.146 + if (path.segmentCount() == 0) {
5.147 + throw new PropertyNotFoundException(xpath, "Schema not specified");
5.148 + }
5.149 + String segment = path.segment(0);
5.150 + int p = segment.indexOf(':');
5.151 + if (p == -1) { // support also other schema paths? like schema.property
5.152 + // allow also unprefixed schemas -> make a search for the first
5.153 + // matching schema having a property with same name as path segment
5.154 + // 0
5.155 + DocumentPart[] parts = getParts();
5.156 + for (DocumentPart part : parts) {
5.157 + if (part.getSchema().hasField(segment)) {
5.158 + return part.resolvePath(path.toString());
5.159 + }
5.160 + }
5.161 + // could not find any matching schema
5.162 + throw new PropertyNotFoundException(xpath, "Schema not specified");
5.163 + }
5.164 + String prefix = segment.substring(0, p);
5.165 + SchemaManager mgr = Framework.getLocalService(SchemaManager.class);
5.166 + Schema schema = mgr.getSchemaFromPrefix(prefix);
5.167 + if (schema == null) {
5.168 + schema = mgr.getSchema(prefix);
5.169 + if (schema == null) {
5.170 + throw new PropertyNotFoundException(xpath,
5.171 + "Could not find registered schema with prefix: "
5.172 + + prefix);
5.173 + }
5.174 + }
5.175 + String[] segments = path.segments();
5.176 + segments[0] = segments[0].substring(p + 1);
5.177 + path = Path.createFromSegments(segments);
5.178 +
5.179 + DocumentPart part = DefaultPropertyFactory.newDocumentPart(schema);
5.180 + part.init((Serializable) dataModels.get(schema.getName()).getMap());
5.181 + if (part == null) {
5.182 + throw new PropertyNotFoundException(
5.183 + xpath,
5.184 + String.format(
5.185 + "Document '%s' with title '%s' and type '%s' does not have any schema with prefix '%s'",
5.186 + getRef(), getTitle(), getType(), prefix));
5.187 + }
5.188 + return part.resolvePath(path.toString());
5.189 + }
5.190 +
5.191 + public Serializable getPropertyValue(String xpath)
5.192 + throws PropertyException, ClientException {
5.193 + return getProperty(xpath).getValue();
5.194 + }
5.195 +
5.196 + public void setPropertyValue(String xpath, Serializable value)
5.197 + throws PropertyException, ClientException {
5.198 + getProperty(xpath).setValue(value);
5.199 + }
5.200 +
5.201 + public DocumentType getDocumentType() {
5.202 + throw new UnsupportedOperationException();
5.203 + }
5.204 +
5.205 + public String getSessionId() {
5.206 + throw new UnsupportedOperationException();
5.207 + }
5.208 +
5.209 + public CoreSession getCoreSession() {
5.210 + throw new UnsupportedOperationException();
5.211 + }
5.212 +
5.213 + public DocumentRef getRef() {
5.214 + throw new UnsupportedOperationException();
5.215 + }
5.216 +
5.217 + public DocumentRef getParentRef() {
5.218 + throw new UnsupportedOperationException();
5.219 + }
5.220 +
5.221 + public String getId() {
5.222 + throw new UnsupportedOperationException();
5.223 + }
5.224 +
5.225 + public String getName() {
5.226 + throw new UnsupportedOperationException();
5.227 + }
5.228 +
5.229 + public String getTitle() throws ClientException {
5.230 + throw new UnsupportedOperationException();
5.231 + }
5.232 +
5.233 + public String getPathAsString() {
5.234 + throw new UnsupportedOperationException();
5.235 + }
5.236 +
5.237 + public Path getPath() {
5.238 + throw new UnsupportedOperationException();
5.239 + }
5.240 +
5.241 + public String getType() {
5.242 + throw new UnsupportedOperationException();
5.243 + }
5.244 +
5.245 + public Set<String> getDeclaredFacets() {
5.246 + throw new UnsupportedOperationException();
5.247 + }
5.248 +
5.249 + public Collection<DataModel> getDataModelsCollection() {
5.250 + throw new UnsupportedOperationException();
5.251 + }
5.252 +
5.253 + public DataModelMap getDataModels() {
5.254 + throw new UnsupportedOperationException();
5.255 + }
5.256 +
5.257 + public DataModel getDataModel(String schema) throws ClientException {
5.258 + throw new UnsupportedOperationException();
5.259 + }
5.260 +
5.261 + public void setPathInfo(String parentPath, String name) {
5.262 + throw new UnsupportedOperationException();
5.263 + }
5.264 +
5.265 + public String getLock() {
5.266 + throw new UnsupportedOperationException();
5.267 + }
5.268 +
5.269 + public boolean isLocked() {
5.270 + throw new UnsupportedOperationException();
5.271 + }
5.272 +
5.273 + public void setLock(String key) throws ClientException {
5.274 + throw new UnsupportedOperationException();
5.275 + }
5.276 +
5.277 + public void unlock() throws ClientException {
5.278 + throw new UnsupportedOperationException();
5.279 + }
5.280 +
5.281 + public ACP getACP() throws ClientException {
5.282 + throw new UnsupportedOperationException();
5.283 + }
5.284 +
5.285 + public void setACP(ACP acp, boolean overwrite) throws ClientException {
5.286 + throw new UnsupportedOperationException();
5.287 + }
5.288 +
5.289 + public boolean hasSchema(String schema) {
5.290 + throw new UnsupportedOperationException();
5.291 + }
5.292 +
5.293 + public boolean hasFacet(String facet) {
5.294 + throw new UnsupportedOperationException();
5.295 + }
5.296 +
5.297 + public boolean isFolder() {
5.298 + throw new UnsupportedOperationException();
5.299 + }
5.300 +
5.301 + public boolean isVersionable() {
5.302 + throw new UnsupportedOperationException();
5.303 + }
5.304 +
5.305 + public boolean isDownloadable() throws ClientException {
5.306 + throw new UnsupportedOperationException();
5.307 + }
5.308 +
5.309 + public boolean isVersion() {
5.310 + throw new UnsupportedOperationException();
5.311 + }
5.312 +
5.313 + public boolean isProxy() {
5.314 + throw new UnsupportedOperationException();
5.315 + }
5.316 +
5.317 + public boolean isImmutable() {
5.318 + throw new UnsupportedOperationException();
5.319 + }
5.320 +
5.321 + public <T> T getAdapter(Class<T> itf) {
5.322 + throw new UnsupportedOperationException();
5.323 + }
5.324 +
5.325 + public <T> T getAdapter(Class<T> itf, boolean refreshCache) {
5.326 + throw new UnsupportedOperationException();
5.327 + }
5.328 +
5.329 + public String getCurrentLifeCycleState() throws ClientException {
5.330 + throw new UnsupportedOperationException();
5.331 + }
5.332 +
5.333 + public String getLifeCyclePolicy() throws ClientException {
5.334 + throw new UnsupportedOperationException();
5.335 + }
5.336 +
5.337 + public boolean followTransition(String transition) throws ClientException {
5.338 + throw new UnsupportedOperationException();
5.339 + }
5.340 +
5.341 + public Collection<String> getAllowedStateTransitions()
5.342 + throws ClientException {
5.343 + throw new UnsupportedOperationException();
5.344 + }
5.345 +
5.346 + public void copyContent(DocumentModel sourceDoc) throws ClientException {
5.347 + throw new UnsupportedOperationException();
5.348 + }
5.349 +
5.350 + public String getRepositoryName() {
5.351 + throw new UnsupportedOperationException();
5.352 + }
5.353 +
5.354 + public String getCacheKey() throws ClientException {
5.355 + throw new UnsupportedOperationException();
5.356 + }
5.357 +
5.358 + public String getSourceId() {
5.359 + throw new UnsupportedOperationException();
5.360 + }
5.361 +
5.362 + public String getVersionLabel() {
5.363 + throw new UnsupportedOperationException();
5.364 + }
5.365 +
5.366 + public Map<String, Serializable> getPrefetch() {
5.367 + throw new UnsupportedOperationException();
5.368 + }
5.369 +
5.370 + public void prefetchProperty(String id, Object value) {
5.371 + throw new UnsupportedOperationException();
5.372 + }
5.373 +
5.374 + public void prefetchCurrentLifecycleState(String lifecycle) {
5.375 + throw new UnsupportedOperationException();
5.376 + }
5.377 +
5.378 + public void prefetchLifeCyclePolicy(String lifeCyclePolicy) {
5.379 + throw new UnsupportedOperationException();
5.380 + }
5.381 +
5.382 + public boolean isLifeCycleLoaded() {
5.383 + throw new UnsupportedOperationException();
5.384 + }
5.385 +
5.386 + public <T extends Serializable> T getSystemProp(String systemProperty,
5.387 + Class<T> type) throws ClientException, DocumentException {
5.388 + throw new UnsupportedOperationException();
5.389 + }
5.390 +
5.391 + public DocumentPart getPart(String schema) throws ClientException {
5.392 + throw new UnsupportedOperationException();
5.393 + }
5.394 +
5.395 + public DocumentPart[] getParts() throws ClientException {
5.396 + throw new UnsupportedOperationException();
5.397 + }
5.398 +
5.399 + public long getFlags() {
5.400 + throw new UnsupportedOperationException();
5.401 + }
5.402 +
5.403 + public void reset() {
5.404 + throw new UnsupportedOperationException();
5.405 + }
5.406 +
5.407 + public void refresh(int refreshFlags, String[] schemas)
5.408 + throws ClientException {
5.409 + throw new UnsupportedOperationException();
5.410 + }
5.411 +
5.412 + public void refresh() throws ClientException {
5.413 + throw new UnsupportedOperationException();
5.414 + }
5.415 +
5.416 + public DocumentModel clone() throws CloneNotSupportedException {
5.417 + throw new UnsupportedOperationException();
5.418 + }
5.419 +
5.420 +}
6.1 new file mode 100644
6.2 --- /dev/null
6.3 +++ b/nuxeo-platform-webapp-base/src/test/java/org/nuxeo/ecm/webapp/bulkedit/TestBulkEditHelper.java
6.4 @@ -0,0 +1,149 @@
6.5 +/*
6.6 + * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
6.7 + *
6.8 + * All rights reserved. This program and the accompanying materials
6.9 + * are made available under the terms of the GNU Lesser General Public License
6.10 + * (LGPL) version 2.1 which accompanies this distribution, and is available at
6.11 + * http://www.gnu.org/licenses/lgpl.html
6.12 + *
6.13 + * This library is distributed in the hope that it will be useful,
6.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6.16 + * Lesser General Public License for more details.
6.17 + *
6.18 + * Contributors:
6.19 + * Thomas Roger
6.20 + */
6.21 +
6.22 +package org.nuxeo.ecm.webapp.bulkedit;
6.23 +
6.24 +import java.util.Arrays;
6.25 +import java.util.List;
6.26 +
6.27 +import org.junit.Test;
6.28 +import org.junit.runner.RunWith;
6.29 +import org.nuxeo.common.collections.ScopedMap;
6.30 +import org.nuxeo.ecm.core.api.ClientException;
6.31 +import org.nuxeo.ecm.core.api.CoreSession;
6.32 +import org.nuxeo.ecm.core.api.DocumentModel;
6.33 +import org.nuxeo.ecm.core.test.CoreFeature;
6.34 +import org.nuxeo.ecm.core.test.annotations.BackendType;
6.35 +import org.nuxeo.ecm.core.test.annotations.RepositoryConfig;
6.36 +import org.nuxeo.ecm.platform.types.TypeManager;
6.37 +import org.nuxeo.runtime.test.runner.Deploy;
6.38 +import org.nuxeo.runtime.test.runner.Features;
6.39 +import org.nuxeo.runtime.test.runner.FeaturesRunner;
6.40 +
6.41 +import com.google.inject.Inject;
6.42 +
6.43 +import static org.junit.Assert.assertEquals;
6.44 +import static org.junit.Assert.assertFalse;
6.45 +import static org.junit.Assert.assertNotNull;
6.46 +import static org.junit.Assert.assertTrue;
6.47 +
6.48 +/**
6.49 + * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
6.50 + */
6.51 +@RunWith(FeaturesRunner.class)
6.52 +@Features(CoreFeature.class)
6.53 +@RepositoryConfig(type = BackendType.H2, user = "Administrator")
6.54 +@Deploy( { "org.nuxeo.ecm.platform.types.api",
6.55 + "org.nuxeo.ecm.platform.types.core", "org.nuxeo.ecm.webapp.base" })
6.56 +public class TestBulkEditHelper {
6.57 +
6.58 + @Inject
6.59 + protected CoreSession session;
6.60 +
6.61 + @Inject
6.62 + protected TypeManager typeManager;
6.63 +
6.64 + @Test
6.65 + public void testCommonSchemas() throws Exception {
6.66 + List<DocumentModel> docs = createTestDocuments();
6.67 + List<String> commonSchemas = BulkEditHelper.getCommonSchemas(docs);
6.68 + assertFalse(commonSchemas.isEmpty());
6.69 + assertEquals(4, commonSchemas.size());
6.70 + assertTrue(commonSchemas.contains("uid"));
6.71 + assertTrue(commonSchemas.contains("dublincore"));
6.72 + assertTrue(commonSchemas.contains("common"));
6.73 + assertTrue(commonSchemas.contains("files"));
6.74 + assertFalse(commonSchemas.contains("note"));
6.75 + assertFalse(commonSchemas.contains("file"));
6.76 + }
6.77 +
6.78 + protected List<DocumentModel> createTestDocuments() throws ClientException {
6.79 + DocumentModel file = session.createDocumentModel("/", "testFile",
6.80 + "File");
6.81 + file.setPropertyValue("dc:title", "testTitle");
6.82 + file = session.createDocument(file);
6.83 + assertNotNull(file);
6.84 + file = session.saveDocument(file);
6.85 +
6.86 + DocumentModel note = session.createDocumentModel("/", "testFile",
6.87 + "Note");
6.88 + note.setPropertyValue("dc:title", "testNote");
6.89 + note = session.createDocument(note);
6.90 + assertNotNull(note);
6.91 + note = session.saveDocument(note);
6.92 + session.save();
6.93 +
6.94 + return Arrays.asList(file,
6.95 + note);
6.96 + }
6.97 +
6.98 + @Test
6.99 + public void testCommonLayouts() throws Exception {
6.100 + List<DocumentModel> docs = createTestDocuments();
6.101 + List<String> commonLayouts = BulkEditHelper.getCommonLayouts(
6.102 + typeManager, docs);
6.103 + assertFalse(commonLayouts.isEmpty());
6.104 + assertEquals(2, commonLayouts.size());
6.105 + assertTrue(commonLayouts.contains("heading"));
6.106 + assertTrue(commonLayouts.contains("dublincore"));
6.107 + assertFalse(commonLayouts.contains("note"));
6.108 + assertFalse(commonLayouts.contains("file"));
6.109 + }
6.110 +
6.111 + @Test
6.112 + public void testGetPropertiesToCopy() throws Exception {
6.113 + DocumentModel doc = FictiveDocumentModel.createFictiveDocumentModelWith("dublincore");
6.114 + ScopedMap map = doc.getContextData();
6.115 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:title", true);
6.116 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:description", false);
6.117 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:coverage dc:subjects", true);
6.118 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:creator", true);
6.119 +
6.120 + List<String> propertiesToCopy = BulkEditHelper.getPropertiesToCopy(doc);
6.121 + assertEquals(4, propertiesToCopy.size());
6.122 + assertTrue(propertiesToCopy.contains("dc:title"));
6.123 + assertTrue(propertiesToCopy.contains("dc:coverage"));
6.124 + assertTrue(propertiesToCopy.contains("dc:subjects"));
6.125 + assertTrue(propertiesToCopy.contains("dc:creator"));
6.126 + assertFalse(propertiesToCopy.contains("dc:description"));
6.127 + }
6.128 +
6.129 + @Test
6.130 + public void testCopyMetadata() throws Exception {
6.131 + List<DocumentModel> docs = createTestDocuments();
6.132 + List<String> commonSchemas = BulkEditHelper.getCommonSchemas(docs);
6.133 + DocumentModel sourceDoc = FictiveDocumentModel.createFictiveDocumentModelWith(commonSchemas);
6.134 + sourceDoc.setProperty("dublincore", "title", "new title");
6.135 + sourceDoc.setProperty("dublincore", "description", "new description");
6.136 + sourceDoc.setProperty("dublincore", "creator", "new creator");
6.137 + sourceDoc.setProperty("dublincore", "source", "new source");
6.138 + ScopedMap map = sourceDoc.getContextData();
6.139 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:title", true);
6.140 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:description", false);
6.141 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:creator", true);
6.142 + map.put(BulkEditHelper.BULK_EDIT_PREFIX + "dc:source", false);
6.143 +
6.144 + BulkEditHelper.copyMetadata(session, sourceDoc, docs);
6.145 + for (DocumentModel doc : docs) {
6.146 + assertEquals("new title", doc.getPropertyValue("dc:title"));
6.147 + assertEquals("new creator", doc.getPropertyValue("dc:creator"));
6.148 + assertFalse("new description".equals(doc.getPropertyValue("dc:description")));
6.149 + assertFalse("new source".equals(doc.getPropertyValue("dc:source")));
6.150 + }
6.151 + }
6.152 +
6.153 +}
7.1 new file mode 100644
7.2 --- /dev/null
7.3 +++ b/nuxeo-platform-webapp-base/src/test/resources/log4j.properties
7.4 @@ -0,0 +1,4 @@
7.5 +log4j.rootLogger=WARN, CONSOLE
7.6 +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
7.7 +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
7.8 +log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%C{1}] %m%n
8.1 --- a/pom.xml
8.2 +++ b/pom.xml
8.3 @@ -244,6 +244,18 @@
8.4 <version>${nuxeo.runtime.version}</version>
8.5 <scope>test</scope>
8.6 </dependency>
8.7 + <dependency>
8.8 + <groupId>org.nuxeo.ecm.core</groupId>
8.9 + <artifactId>nuxeo-core-test</artifactId>
8.10 + <version>${nuxeo.core.version}</version>
8.11 + <scope>test</scope>
8.12 + </dependency>
8.13 + <dependency>
8.14 + <groupId>org.nuxeo.ecm.platform</groupId>
8.15 + <artifactId>nuxeo-platform-types-core</artifactId>
8.16 + <version>${nuxeo.services.version}</version>
8.17 + <scope>test</scope>
8.18 + </dependency>
8.19 </dependencies>
8.20 </dependencyManagement>
8.21