001/*
002 * (C) Copyright 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 *     Funsho David
018 *     Kevin Leturc
019 *
020 */
021
022package org.nuxeo.ecm.core.versioning;
023
024import org.apache.commons.lang.StringUtils;
025import org.jboss.el.ExpressionFactoryImpl;
026import org.nuxeo.ecm.core.api.DocumentModel;
027import org.nuxeo.ecm.core.api.NuxeoPrincipal;
028import org.nuxeo.ecm.core.api.local.ClientLoginModule;
029import org.nuxeo.ecm.core.schema.DocumentType;
030import org.nuxeo.ecm.platform.el.ELService;
031import org.nuxeo.runtime.api.Framework;
032
033import javax.el.ELContext;
034import javax.el.ExpressionFactory;
035import javax.el.ValueExpression;
036import javax.el.VariableMapper;
037
038import java.util.Collection;
039
040import static org.nuxeo.ecm.platform.el.ELConstants.CURRENT_DOCUMENT;
041import static org.nuxeo.ecm.platform.el.ELConstants.CURRENT_USER;
042import static org.nuxeo.ecm.platform.el.ELConstants.DOCUMENT;
043import static org.nuxeo.ecm.platform.el.ELConstants.PREVIOUS_DOCUMENT;
044import static org.nuxeo.ecm.platform.el.ELConstants.PRINCIPAL;
045
046/**
047 * @since 9.1
048 */
049public class StandardVersioningPolicyFilter implements VersioningPolicyFilter {
050
051    protected Collection<String> types;
052
053    protected Collection<String> facets;
054
055    protected Collection<String> schemas;
056
057    protected String condition;
058
059    public StandardVersioningPolicyFilter(Collection<String> types, Collection<String> facets,
060            Collection<String> schemas, String condition) {
061        this.types = types;
062        this.facets = facets;
063        this.schemas = schemas;
064        this.condition = condition;
065    }
066
067    @Override
068    public boolean test(DocumentModel previousDocument, DocumentModel currentDocument) {
069        if (!types.isEmpty() && !types.contains(currentDocument.getType())) {
070            return false;
071        }
072        DocumentType docType = currentDocument.getDocumentType();
073        if (!schemas.isEmpty() && schemas.stream().noneMatch(docType::hasSchema)) {
074            return false;
075        }
076        if (!facets.isEmpty() && facets.stream().noneMatch(docType::hasFacet)) {
077            return false;
078        }
079        if (!StringUtils.isBlank(condition)) {
080
081            String cond = evaluateCondition(condition);
082
083            ELContext context = Framework.getService(ELService.class).createELContext();
084            ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
085
086            VariableMapper vm = context.getVariableMapper();
087
088            // init default variables
089            ValueExpression previousDocExpr = expressionFactory.createValueExpression(previousDocument,
090                    DocumentModel.class);
091            ValueExpression currentDocExpr = expressionFactory.createValueExpression(currentDocument,
092                    DocumentModel.class);
093            ValueExpression userExpr = expressionFactory.createValueExpression(ClientLoginModule.getCurrentPrincipal(),
094                    NuxeoPrincipal.class);
095            vm.setVariable(PREVIOUS_DOCUMENT, previousDocExpr);
096            vm.setVariable(CURRENT_DOCUMENT, currentDocExpr);
097            vm.setVariable(DOCUMENT, currentDocExpr);
098            vm.setVariable(PRINCIPAL, userExpr);
099            vm.setVariable(CURRENT_USER, userExpr);
100
101            // evaluate expression
102            ValueExpression ve = expressionFactory.createValueExpression(context, cond, Boolean.class);
103            return Boolean.TRUE.equals(ve.getValue(context));
104        }
105        return true;
106    }
107
108    /**
109     * Evaluate and build a valid condition
110     *
111     * @param condition the initial condition
112     */
113    public static String evaluateCondition(String condition) {
114
115        String cond = condition.trim();
116        // compatibility code, as JEXL could resolve that kind of expression:
117        // detect if expression is in brackets #{}, otherwise add it
118        if (!cond.startsWith("#{") && !cond.startsWith("${") && !cond.endsWith("}")) {
119            cond = "#{" + cond + "}";
120        }
121
122        // Check if there is a null/not-null evaluation on previousDocument, if not
123        // Add a not-null evaluation on it to prevent NPE
124        String p1 = ".*" + PREVIOUS_DOCUMENT + "\\..+";
125        String p2 = ".*" + PREVIOUS_DOCUMENT + "\\s*[!=]=\\s*null.*";
126        if (cond.matches(p1) && !cond.matches(p2)) {
127            cond = "#{" + PREVIOUS_DOCUMENT + " != null && (" + cond.substring(2, cond.length() - 1) + ")}";
128        }
129        return cond;
130    }
131}