001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Thomas Roger
018 */
019
020package org.nuxeo.ecm.webapp.bulkedit;
021
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Locale;
026import java.util.Map;
027
028import org.apache.commons.lang.StringUtils;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.core.api.VersioningOption;
034import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
035import org.nuxeo.ecm.core.versioning.VersioningService;
036import org.nuxeo.runtime.model.ComponentInstance;
037import org.nuxeo.runtime.model.DefaultComponent;
038
039/**
040 * Default implementation of {@link BulkEditService}.
041 *
042 * @since 5.7.3
043 */
044public class BulkEditServiceImpl extends DefaultComponent implements BulkEditService {
045
046    public static final String VERSIONING_EP = "versioning";
047
048    public static final VersioningOption DEFAULT_VERSIONING_OPTION = VersioningOption.MINOR;
049
050    private static final Log log = LogFactory.getLog(BulkEditServiceImpl.class);
051
052    protected VersioningOption defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
053
054    @Override
055    public void updateDocuments(CoreSession session, DocumentModel sourceDoc, List<DocumentModel> targetDocs)
056            {
057        List<String> propertiesToCopy = getPropertiesToCopy(sourceDoc);
058        if (propertiesToCopy.isEmpty()) {
059            return;
060        }
061
062        for (DocumentModel targetDoc : targetDocs) {
063
064            for (String propertyToCopy : propertiesToCopy) {
065                try {
066                    targetDoc.setPropertyValue(propertyToCopy, sourceDoc.getPropertyValue(propertyToCopy));
067                } catch (PropertyNotFoundException e) {
068                    String message = "%s property does not exist on %s";
069                    log.warn(String.format(message, propertyToCopy, targetDoc));
070                }
071            }
072            targetDoc.putContextData(VersioningService.VERSIONING_OPTION, defaultVersioningOption);
073            session.saveDocument(targetDoc);
074        }
075    }
076
077    /**
078     * Extracts the properties to be copied from {@code sourceDoc}. The properties are stored in the ContextData of
079     * {@code sourceDoc}: the key is the xpath property, the value is {@code true} if the property has to be copied,
080     * {@code false otherwise}.
081     */
082    protected List<String> getPropertiesToCopy(DocumentModel sourceDoc) {
083        List<String> propertiesToCopy = new ArrayList<String>();
084        for (Map.Entry<String, Serializable> entry : sourceDoc.getContextData().entrySet()) {
085            String key = entry.getKey();
086            if (key.startsWith(BULK_EDIT_PREFIX)) {
087                String[] properties = key.replace(BULK_EDIT_PREFIX, "").split(" ");
088                Serializable value = entry.getValue();
089                if (value instanceof Boolean && (Boolean) value) {
090                    for (String property : properties) {
091                        if (!property.startsWith(CONTEXT_DATA)) {
092                            propertiesToCopy.add(property);
093                        }
094                    }
095                }
096            }
097        }
098        return propertiesToCopy;
099    }
100
101    /**
102     * @deprecated since 7.3. The option is passed to the CoreSession#saveDocument method.
103     */
104    @Deprecated
105    protected void checkIn(DocumentModel doc) {
106        if (defaultVersioningOption != null && defaultVersioningOption != VersioningOption.NONE) {
107            if (doc.isCheckedOut()) {
108                doc.checkIn(defaultVersioningOption, null);
109            }
110        }
111    }
112
113    @Override
114    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
115        if (VERSIONING_EP.equals(extensionPoint)) {
116            VersioningDescriptor desc = (VersioningDescriptor) contribution;
117            String defaultVer = desc.getDefaultVersioningOption();
118            if (!StringUtils.isBlank(defaultVer)) {
119                try {
120                    defaultVersioningOption = VersioningOption.valueOf(defaultVer.toUpperCase(Locale.ENGLISH));
121                } catch (IllegalArgumentException e) {
122                    log.warn(String.format("Illegal versioning option: %s, using %s instead", defaultVer,
123                            DEFAULT_VERSIONING_OPTION));
124                    defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
125                }
126            }
127        }
128    }
129
130    @Override
131    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
132        if (VERSIONING_EP.equals(extensionPoint)) {
133            defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
134        }
135    }
136
137}