001/*
002 * (C) Copyright 2006-2019 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 *     Bogdan Stefanescu
018 *     Ricardo Dias
019 *     Estelle Giuly
020 */
021package org.nuxeo.ecm.automation.features;
022
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.List;
026import java.util.Set;
027
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.apache.commons.text.StringEscapeUtils;
032import org.nuxeo.ecm.automation.core.scripting.CoreFunctions;
033import org.nuxeo.ecm.automation.core.util.StringList;
034import org.nuxeo.ecm.core.api.CoreSession;
035import org.nuxeo.ecm.core.api.DocumentModel;
036import org.nuxeo.ecm.core.api.DocumentRef;
037import org.nuxeo.ecm.core.api.IdRef;
038import org.nuxeo.ecm.core.api.NuxeoPrincipal;
039import org.nuxeo.ecm.core.api.PathRef;
040import org.nuxeo.ecm.core.query.sql.NXQL;
041import org.nuxeo.ecm.core.uidgen.UIDGeneratorService;
042import org.nuxeo.ecm.core.uidgen.UIDSequencer;
043import org.nuxeo.ecm.directory.Session;
044import org.nuxeo.ecm.directory.api.DirectoryService;
045import org.nuxeo.ecm.platform.usermanager.UserManager;
046import org.nuxeo.runtime.api.Framework;
047import org.nuxeo.runtime.services.config.ConfigurationService;
048
049/**
050 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
051 */
052public class PlatformFunctions extends CoreFunctions {
053
054    private static final Log log = LogFactory.getLog(PlatformFunctions.class);
055
056    public static final String HIBERNATE_SEQUENCER_PROPERTY = "org.nuxeo.ecm.core.uidgen.sequencer.hibernate";
057
058    public UserManager getUserManager() {
059        return Framework.getService(UserManager.class);
060    }
061
062    /**
063     * @deprecated since 11.1. Use {@link Framework#getService(Class)} with {@link DirectoryService} instead.
064     */
065    @Deprecated
066    public DirectoryService getDirService() {
067        return Framework.getService(DirectoryService.class);
068    }
069
070    public String getVocabularyLabel(String voc, String key) {
071        DirectoryService directoryService = Framework.getService(DirectoryService.class);
072        try (Session session = directoryService.open(voc)) {
073            if (!session.hasEntry(key)) {
074                log.debug("Unable to find the key '" + key + "' in the vocabulary '" + voc + "'.");
075                return key;
076            }
077            DocumentModel doc = session.getEntry(key);
078            return (String) doc.getPropertyValue("label"); // no schema prefix for vocabularies
079        }
080    }
081
082    public NuxeoPrincipal getPrincipal(String username) {
083        return getUserManager().getPrincipal(username);
084    }
085
086    protected String getEmail(NuxeoPrincipal principal, String userSchemaName, String userEmailFieldName) {
087        if (principal == null) {
088            return null;
089        }
090        return (String) principal.getModel().getProperty(userSchemaName, userEmailFieldName);
091    }
092
093    public String getEmail(String username) {
094        UserManager userManager = getUserManager();
095        return getEmail(userManager.getPrincipal(username), userManager.getUserSchemaName(),
096                userManager.getUserEmailField());
097    }
098
099    public Set<NuxeoPrincipal> getPrincipalsFromGroup(String group) {
100        return getPrincipalsFromGroup(group, false);
101    }
102
103    public Set<NuxeoPrincipal> getPrincipalsFromGroup(String group, boolean ignoreGroups) {
104        PrincipalHelper ph = new PrincipalHelper(getUserManager(), null);
105        return ph.getPrincipalsFromGroup(group, !ignoreGroups);
106    }
107
108    public StringList getEmailsFromGroup(String group) {
109        return getEmailsFromGroup(group, false);
110    }
111
112    public StringList getEmailsFromGroup(String group, boolean ignoreGroups) {
113        PrincipalHelper ph = new PrincipalHelper(getUserManager(), null);
114        Set<String> emails = ph.getEmailsFromGroup(group, !ignoreGroups);
115        return new StringList(emails);
116    }
117
118    public StringList getPrincipalEmails(List<NuxeoPrincipal> principals) {
119        StringList result = new StringList(principals.size());
120        String schemaName = getUserManager().getUserSchemaName();
121        String fieldName = getUserManager().getUserEmailField();
122        for (NuxeoPrincipal principal : principals) {
123            String email = getEmail(principal, schemaName, fieldName);
124            if (!StringUtils.isEmpty(email)) {
125                result.add(email);
126            }
127        }
128        return result;
129    }
130
131    public StringList getEmails(List<String> usernames) {
132        return getEmails(usernames, false);
133    }
134
135    /**
136     * Returns user emails
137     *
138     * @param usernames list of user names
139     * @param usePrefix indicates if user resolution takes into account nuxeo prefix <b>user:</b>
140     * @since 5.5
141     */
142    public StringList getEmails(List<String> usernames, boolean usePrefix) {
143        if (usernames == null) {
144            return new StringList(0);
145        }
146        UserManager userManager = getUserManager();
147        StringList result = new StringList(usernames.size());
148        String schemaName = getUserManager().getUserSchemaName();
149        String fieldName = getUserManager().getUserEmailField();
150        for (String username : usernames) {
151            NuxeoPrincipal principal = null;
152            if (usePrefix) {
153                if (username.startsWith(NuxeoPrincipal.PREFIX)) {
154                    principal = userManager.getPrincipal(username.replace(NuxeoPrincipal.PREFIX, ""));
155                }
156            } else {
157                principal = userManager.getPrincipal(username);
158            }
159            if (principal != null) {
160                String email = getEmail(principal, schemaName, fieldName);
161                if (!StringUtils.isEmpty(email)) {
162                    result.add(email);
163                }
164            }
165        }
166        return result;
167    }
168
169    public String getNextId(final String key) {
170        ConfigurationService configurationService = Framework.getService(ConfigurationService.class);
171        boolean useHibernate = configurationService.isBooleanTrue(HIBERNATE_SEQUENCER_PROPERTY);
172        return getNextId(key, useHibernate ? "hibernateSequencer" : null);
173    }
174
175    public String getNextId(final String key, final String sequencerName) {
176        UIDGeneratorService uidGeneratorService = Framework.getService(UIDGeneratorService.class);
177        UIDSequencer seq = uidGeneratorService.getSequencer(sequencerName);
178        return Long.toString(seq.getNextLong(key));
179    }
180
181    public String htmlEscape(String str) {
182        return StringEscapeUtils.escapeHtml4(str);
183    }
184
185    /**
186     * Escapes a string according to NXQL escaping rules.
187     * <p>
188     * The resulting string is safe to include between single quotes in a NXQL expression.
189     *
190     * @param str the input string
191     * @return the escaped string
192     * @since 6.0-HF21, 7.10
193     */
194    public String nxqlEscape(String str) {
195        return NXQL.escapeStringInner(str);
196    }
197
198    /**
199     * Concatenate into the list given as first argument other arguments. Other arguments will be explosed if it is a
200     * list of object. ex: concatenateInto(myList, a, anotherList) with a is scalar object and anotherList is a list of
201     * object will produced myList.add(a) and the same for each object contained into the anotherList list.
202     *
203     * @param list List of values of type A
204     * @param values Value can be instance of {@code Collection<Object>} or an array of {@code Objects} or simply a
205     *            scalar {@code Object}. If Null, the parameter is ignored
206     * @return the list that contains the list contain and value (see value description)
207     * @throws ClassCastException if in values there is at least one object type not compatible with the collection list
208     */
209    @SuppressWarnings("unchecked")
210    public <T> List<T> concatenateIntoList(List<T> list, Object... values) {
211
212        if (list == null) {
213            throw new IllegalArgumentException("First parameter must not be null");
214        }
215
216        for (Object value : values) {
217            if (value == null) {
218                continue;
219            }
220
221            if (value instanceof Object[]) {
222                for (Object subValue : (Object[]) value) {
223                    if (subValue != null) {
224                        list.add((T) subValue);
225                    }
226                }
227                continue;
228            }
229
230            if (value instanceof Collection) {
231                for (Object subValue : (Collection<Object>) value) {
232                    if (subValue != null) {
233                        list.add((T) subValue);
234                    }
235                }
236                continue;
237            }
238
239            list.add((T) value);
240
241        }
242        return list;
243    }
244
245    /**
246     * Idem than concatenateInto except that a new list is created.
247     */
248    public <T> List<T> concatenateValuesAsNewList(Object... values) {
249
250        List<T> result = new ArrayList<>();
251        return concatenateIntoList(result, values);
252    }
253
254    /**
255     * Checks if a document with the supplied id (or path) exists.
256     *
257     * @param session The CoreSession to obtain the document
258     * @param idOrPath The document Id or path
259     * @return true if the document exists, or false otherwise
260     */
261    public boolean documentExists(CoreSession session, String idOrPath) {
262        DocumentRef documentRef = idOrPath.startsWith("/") ? new PathRef(idOrPath) : new IdRef(idOrPath);
263        return session.exists(documentRef);
264    }
265
266}