001/*
002 * (C) Copyright 2013-2017 Nuxeo (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 */
019package org.nuxeo.ecm.webapp.bulkedit;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Locale;
025import java.util.Map;
026
027import org.apache.commons.lang.StringUtils;
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.VersioningOption;
033import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
034import org.nuxeo.runtime.api.Framework;
035import org.nuxeo.runtime.logging.DeprecationLogger;
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    /**
049     * @deprecated since 9.1 automatic versioning mechanism has been moved to versioning service, this field is not used
050     * anymore
051     */
052    @Deprecated
053    public static final VersioningOption DEFAULT_VERSIONING_OPTION = VersioningOption.MINOR;
054
055    private static final Log log = LogFactory.getLog(BulkEditServiceImpl.class);
056
057    /**
058     * @deprecated since 9.1 automatic versioning mechanism has been moved to versioning service, this field is not used
059     * anymore
060     */
061    @Deprecated
062    protected VersioningOption defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
063
064    @Override
065    public void updateDocuments(CoreSession session, DocumentModel sourceDoc, List<DocumentModel> targetDocs)
066            {
067        List<String> propertiesToCopy = getPropertiesToCopy(sourceDoc);
068        if (propertiesToCopy.isEmpty()) {
069            return;
070        }
071
072        for (DocumentModel targetDoc : targetDocs) {
073
074            for (String propertyToCopy : propertiesToCopy) {
075                try {
076                    targetDoc.setPropertyValue(propertyToCopy, sourceDoc.getPropertyValue(propertyToCopy));
077                } catch (PropertyNotFoundException e) {
078                    String message = "%s property does not exist on %s";
079                    log.warn(String.format(message, propertyToCopy, targetDoc));
080                }
081            }
082            targetDoc.putContextData(CoreSession.SOURCE, "bulkEdit");
083            session.saveDocument(targetDoc);
084        }
085    }
086
087    /**
088     * Extracts the properties to be copied from {@code sourceDoc}. The properties are stored in the ContextData of
089     * {@code sourceDoc}: the key is the xpath property, the value is {@code true} if the property has to be copied,
090     * {@code false otherwise}.
091     */
092    protected List<String> getPropertiesToCopy(DocumentModel sourceDoc) {
093        List<String> propertiesToCopy = new ArrayList<>();
094        for (Map.Entry<String, Serializable> entry : sourceDoc.getContextData().entrySet()) {
095            String key = entry.getKey();
096            if (key.startsWith(BULK_EDIT_PREFIX)) {
097                String[] properties = key.replace(BULK_EDIT_PREFIX, "").split(" ");
098                Serializable value = entry.getValue();
099                if (value instanceof Boolean && (Boolean) value) {
100                    for (String property : properties) {
101                        if (!property.startsWith(CONTEXT_DATA)) {
102                            propertiesToCopy.add(property);
103                        }
104                    }
105                }
106            }
107        }
108        return propertiesToCopy;
109    }
110
111    /**
112     * @deprecated since 7.3. The option is passed to the CoreSession#saveDocument method.
113     */
114    @Deprecated
115    protected void checkIn(DocumentModel doc) {
116        if (defaultVersioningOption != null && defaultVersioningOption != VersioningOption.NONE) {
117            if (doc.isCheckedOut()) {
118                doc.checkIn(defaultVersioningOption, null);
119            }
120        }
121    }
122
123    @Override
124    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
125        if (VERSIONING_EP.equals(extensionPoint)) {
126            String message = "Extension point 'versioning' has been deprecated and corresponding behavior removed from "
127                    + "Nuxeo Platform. Please use versioning policy instead.";
128            DeprecationLogger.log(message, "9.1");
129            Framework.getRuntime().getMessageHandler().addWarning(message);
130            VersioningDescriptor desc = (VersioningDescriptor) contribution;
131            String defaultVer = desc.getDefaultVersioningOption();
132            if (!StringUtils.isBlank(defaultVer)) {
133                try {
134                    defaultVersioningOption = VersioningOption.valueOf(defaultVer.toUpperCase(Locale.ENGLISH));
135                } catch (IllegalArgumentException e) {
136                    log.warn(String.format("Illegal versioning option: %s, using %s instead", defaultVer,
137                            DEFAULT_VERSIONING_OPTION));
138                    defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
139                }
140            }
141        }
142    }
143
144    @Override
145    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
146        if (VERSIONING_EP.equals(extensionPoint)) {
147            defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
148        }
149    }
150
151}