001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Thomas Roger
016 */
017
018package org.nuxeo.ecm.webapp.bulkedit;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Locale;
024import java.util.Map;
025
026import org.apache.commons.lang.StringUtils;
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.CoreSession;
030import org.nuxeo.ecm.core.api.DocumentModel;
031import org.nuxeo.ecm.core.api.VersioningOption;
032import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
033import org.nuxeo.ecm.core.versioning.VersioningService;
034import org.nuxeo.runtime.model.ComponentInstance;
035import org.nuxeo.runtime.model.DefaultComponent;
036
037/**
038 * Default implementation of {@link BulkEditService}.
039 *
040 * @since 5.7.3
041 */
042public class BulkEditServiceImpl extends DefaultComponent implements BulkEditService {
043
044    public static final String VERSIONING_EP = "versioning";
045
046    public static final VersioningOption DEFAULT_VERSIONING_OPTION = VersioningOption.MINOR;
047
048    private static final Log log = LogFactory.getLog(BulkEditServiceImpl.class);
049
050    protected VersioningOption defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
051
052    @Override
053    public void updateDocuments(CoreSession session, DocumentModel sourceDoc, List<DocumentModel> targetDocs)
054            {
055        List<String> propertiesToCopy = getPropertiesToCopy(sourceDoc);
056        if (propertiesToCopy.isEmpty()) {
057            return;
058        }
059
060        for (DocumentModel targetDoc : targetDocs) {
061
062            for (String propertyToCopy : propertiesToCopy) {
063                try {
064                    targetDoc.setPropertyValue(propertyToCopy, sourceDoc.getPropertyValue(propertyToCopy));
065                } catch (PropertyNotFoundException e) {
066                    String message = "%s property does not exist on %s";
067                    log.warn(String.format(message, propertyToCopy, targetDoc));
068                }
069            }
070            targetDoc.putContextData(VersioningService.VERSIONING_OPTION, defaultVersioningOption);
071            session.saveDocument(targetDoc);
072        }
073    }
074
075    /**
076     * Extracts the properties to be copied from {@code sourceDoc}. The properties are stored in the ContextData of
077     * {@code sourceDoc}: the key is the xpath property, the value is {@code true} if the property has to be copied,
078     * {@code false otherwise}.
079     */
080    protected List<String> getPropertiesToCopy(DocumentModel sourceDoc) {
081        List<String> propertiesToCopy = new ArrayList<String>();
082        for (Map.Entry<String, Serializable> entry : sourceDoc.getContextData().entrySet()) {
083            String key = entry.getKey();
084            if (key.startsWith(BULK_EDIT_PREFIX)) {
085                String[] properties = key.replace(BULK_EDIT_PREFIX, "").split(" ");
086                Serializable value = entry.getValue();
087                if (value instanceof Boolean && (Boolean) value) {
088                    for (String property : properties) {
089                        if (!property.startsWith(CONTEXT_DATA)) {
090                            propertiesToCopy.add(property);
091                        }
092                    }
093                }
094            }
095        }
096        return propertiesToCopy;
097    }
098
099    /**
100     * @deprecated since 7.3. The option is passed to the CoreSession#saveDocument method.
101     */
102    @Deprecated
103    protected void checkIn(DocumentModel doc) {
104        if (defaultVersioningOption != null && defaultVersioningOption != VersioningOption.NONE) {
105            if (doc.isCheckedOut()) {
106                doc.checkIn(defaultVersioningOption, null);
107            }
108        }
109    }
110
111    @Override
112    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
113        if (VERSIONING_EP.equals(extensionPoint)) {
114            VersioningDescriptor desc = (VersioningDescriptor) contribution;
115            String defaultVer = desc.getDefaultVersioningOption();
116            if (!StringUtils.isBlank(defaultVer)) {
117                try {
118                    defaultVersioningOption = VersioningOption.valueOf(defaultVer.toUpperCase(Locale.ENGLISH));
119                } catch (IllegalArgumentException e) {
120                    log.warn(String.format("Illegal versioning option: %s, using %s instead", defaultVer,
121                            DEFAULT_VERSIONING_OPTION));
122                    defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
123                }
124            }
125        }
126    }
127
128    @Override
129    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
130        if (VERSIONING_EP.equals(extensionPoint)) {
131            defaultVersioningOption = DEFAULT_VERSIONING_OPTION;
132        }
133    }
134
135}