001/*
002 *  Copyright 2015 Anyware Services
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 */
016package org.ametys.plugins.core.user;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.LinkedHashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Objects;
026import java.util.Optional;
027import java.util.Set;
028import java.util.stream.Collectors;
029
030import javax.xml.transform.TransformerException;
031
032import org.apache.avalon.framework.component.Component;
033import org.apache.avalon.framework.configuration.Configuration;
034import org.apache.avalon.framework.configuration.ConfigurationException;
035import org.apache.avalon.framework.configuration.ConfigurationUtil;
036import org.apache.avalon.framework.service.ServiceException;
037import org.apache.avalon.framework.service.ServiceManager;
038import org.apache.avalon.framework.service.Serviceable;
039import org.apache.cocoon.xml.AttributesImpl;
040import org.apache.cocoon.xml.XMLUtils;
041import org.apache.xpath.XPathAPI;
042import org.w3c.dom.Element;
043import org.w3c.dom.Node;
044import org.xml.sax.ContentHandler;
045import org.xml.sax.SAXException;
046
047import org.ametys.core.user.User;
048import org.ametys.core.user.UserIdentity;
049import org.ametys.core.user.UserManager;
050import org.ametys.core.user.directory.NotUniqueUserException;
051import org.ametys.core.user.directory.UserDirectoryFactory;
052import org.ametys.core.user.population.UserPopulationDAO;
053import org.ametys.runtime.plugin.component.AbstractLogEnabled;
054
055/**
056 * Simple user helper, for common function working on {@link User}
057 */
058public class UserHelper extends AbstractLogEnabled implements Component, Serviceable
059{
060    /** The Avalon role */
061    public static final String ROLE = UserHelper.class.getName();
062    
063    /** The user population DAO */
064    private UserPopulationDAO _userPopulationDAO;
065
066    /** The user directory factory */
067    private UserDirectoryFactory _userDirectoryFactory;
068
069    private UserManager _userManager;
070    
071    @Override
072    public void service(ServiceManager smanager) throws ServiceException
073    {
074        _userManager = (UserManager) smanager.lookup(UserManager.ROLE);
075        _userPopulationDAO = (UserPopulationDAO) smanager.lookup(UserPopulationDAO.ROLE);
076        _userDirectoryFactory = (UserDirectoryFactory) smanager.lookup(UserDirectoryFactory.ROLE);
077    }
078    
079    /**
080     * Get the user's full name handling request cache
081     * @param userIdentity The user identity
082     * @return user's full name or null if user does not exist
083     */
084    public String getUserFullName (UserIdentity userIdentity)
085    {
086        User user = _userManager.getUser(userIdentity);
087        return user != null ? user.getFullName() : null;
088    }
089    
090    /**
091     * Get the user's sortable name handling request cache
092     * @param userIdentity The user identity
093     * @return user's full name or null if user does not exist
094     */
095    public String getUserSortableName (UserIdentity userIdentity)
096    {
097        User user = _userManager.getUser(userIdentity);
098        return user != null ? user.getSortableName() : null;
099    }
100    
101    /**
102     * Populate a list of map, where each map representing an user.
103     * @param users The list of users
104     * @return The list of map.
105     */
106    public List<Map<String, Object>> userIdentities2json(Collection<UserIdentity> users)
107    {
108        return users.stream().map(identity -> _userManager.getUser(identity)).filter(Objects::nonNull).map(user -> user2json(user)).collect(Collectors.toList());
109    }
110    
111    
112    /**
113     * Populate a list of map, where each map representing an user.
114     * @param users The list of users
115     * @return The list of map.
116     */
117    public List<Map<String, Object>> users2json(Collection<User> users)
118    {
119        return users2json(users, false);
120    }
121    
122    /**
123     * Populate a list of map, where each map representing an user.
124     * @param users The list of users
125     * @param full Set to <code>true</code> to get full information on user
126     * @return The list of map.
127     */
128    public List<Map<String, Object>> users2json(Collection<User> users, boolean full)
129    {
130        List<Map<String, Object>> userList = new ArrayList<>();
131        
132        Set<User> distinctUsers = new LinkedHashSet<>(users);
133        for (User user : distinctUsers)
134        {
135            userList.add(user2json(user, full));
136        }
137        
138        return userList;
139    }
140        
141    /**
142     * Get the JSON object representing a user
143     * @param userIdentity The user identity
144     * @return The user as JSON object
145     */
146    public Map<String, Object> user2json (UserIdentity userIdentity)
147    {
148        return user2json(userIdentity, false);
149    }
150    
151    /**
152     * Get the JSON object representing a user
153     * @param userIdentity The user identity
154     * @param full Set to <code>true</code> to get full information on user
155     * @return The user as JSON object
156     */
157    public Map<String, Object> user2json (UserIdentity userIdentity, boolean full)
158    {
159        User user = _userManager.getUser(userIdentity);
160        if (user != null)
161        {
162            return user2json(user, full);
163        }
164        return Collections.EMPTY_MAP;
165    }
166    
167    /**
168     * Get the JSON object representing a user
169     * @param user The user
170     * @return The user as JSON object
171     */
172    public Map<String, Object> user2json(User user)
173    {
174        return _user2json(user, false);
175    }
176    
177    /**
178     * Get the JSON object representing a user
179     * @param user The user
180     * @param full Set to <code>true</code> to get full information on user
181     * @return The user as JSON object or null, if the given user is null
182     */
183    public Map<String, Object> user2json(User user, boolean full)
184    {
185        return _user2json(user, full);
186    }
187    
188    private Map<String, Object> _user2json(User user, boolean full)
189    {
190        if (user == null)
191        {
192            return null;
193        }
194        
195        Map<String, Object> userInfos = new HashMap<>();
196        
197        userInfos.put("login", user.getIdentity().getLogin());
198        
199        String populationId = user.getIdentity().getPopulationId();
200        userInfos.put("populationId", populationId);
201        
202        userInfos.put("fullname", user.getFullName());
203        userInfos.put("sortablename", user.getSortableName());
204        
205        if (full)
206        {
207            String udModelId = user.getUserDirectory() != null ? user.getUserDirectory().getUserDirectoryModelId() : "";
208            
209            userInfos.put("populationLabel", _userPopulationDAO.getUserPopulation(populationId).getLabel());
210            userInfos.put("directory", _userDirectoryFactory.hasExtension(udModelId) ? _userDirectoryFactory.getExtension(udModelId).getLabel() : "");
211            
212            userInfos.put("lastname", user.getLastName());
213            userInfos.put("firstname", user.getFirstName());
214            userInfos.put("email", user.getEmail());
215        }
216        
217        return userInfos;
218    }
219    
220    /**
221     * Get the user identity from a JSON object
222     * @param json the JSON object representing the user
223     * @return the user identity
224     */
225    public UserIdentity json2userIdentity(Map<String, ? extends Object> json)
226    {
227        // If the user is empty, return null
228        return Optional.ofNullable(json)
229                .filter(u -> !u.isEmpty() && u.get("login") != null && u.get("populationId") != null)
230                .map(u -> new UserIdentity((String) u.get("login"), (String) u.get("populationId")))
231                .orElse(null);
232    }
233    
234    /**
235     * Get the user from its identity
236     * @param userIdentity The user identity
237     * @return The user or null if not found
238     * @deprecated use {@link UserManager#getUser(UserIdentity)} instead
239     */
240    @Deprecated
241    public User getUser (UserIdentity userIdentity)
242    {
243        if (userIdentity == null)
244        {
245            return null;
246        }
247         
248        return _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
249    }
250    
251    /**
252     * Get the user from its email
253     * @param populationId The population id
254     * @param email The email
255     * @return The user or null if not found or if multiple user were found with this email
256     * @deprecated use {@link UserManager#getUserByEmail(String, String)} instead
257     */
258    @Deprecated
259    public User getUserByEmail (String populationId, String email)
260    {
261        if (email == null)
262        {
263            return null;
264        }
265
266        try
267        {
268            return _userManager.getUserByEmail(populationId, email);
269        }
270        catch (NotUniqueUserException e)
271        {
272            return null;
273        }
274    }
275
276    /**
277     * SAX an user identity
278     * @param userIdentity The user identity to SAX
279     * @param handler The content handler
280     * @throws SAXException If a SAX error occurs
281     */
282    public void saxUserIdentity(UserIdentity userIdentity, ContentHandler handler) throws SAXException
283    {
284        saxUserIdentity(userIdentity, handler, "user");
285    }
286    
287    /**
288     * SAX an user identity
289     * @param userIdentity The user identity to SAX
290     * @param handler The content handler
291     * @param tagName The XML tag for saxed user
292     * @throws SAXException If a SAX error occurs
293     */
294    public void saxUserIdentity(UserIdentity userIdentity, ContentHandler handler, String tagName) throws SAXException
295    {
296        User user = _userManager.getUser(userIdentity);
297        if (user != null)
298        {
299            saxUser(user, handler, tagName);
300        }
301        else
302        {
303            getLogger().warn("Unable to sax unknown user with identity {}", userIdentity);
304        }
305           
306    }
307    
308    /**
309     * SAX an user
310     * @param user The user. If null no sax events will be generated.
311     * @param handler The content handler
312     * @throws SAXException If a SAX error occurs
313     */
314    public void saxUser(User user, ContentHandler handler) throws SAXException
315    {
316        saxUser(user, handler, "user");
317    }
318    
319    /**
320     * SAX an user
321     * @param user The user. If null no sax events will be generated.
322     * @param handler The content handler
323     * @param tagName The XML tag for saxed user
324     * @throws SAXException If a SAX error occurs
325     */
326    public void saxUser(User user, ContentHandler handler, String tagName) throws SAXException
327    {
328        if (user != null)
329        {
330            AttributesImpl attr = new AttributesImpl();
331            attr.addCDATAAttribute("login", user.getIdentity().getLogin());
332            attr.addCDATAAttribute("population", user.getIdentity().getPopulationId());
333            
334            XMLUtils.startElement(handler, tagName, attr);
335            
336            XMLUtils.createElement(handler, "lastname", user.getLastName());
337            XMLUtils.createElement(handler, "firstname", user.getFirstName());
338            XMLUtils.createElement(handler, "email", user.getEmail());
339            
340            XMLUtils.createElement(handler, "fullname", user.getFullName());
341            XMLUtils.createElement(handler, "sortablename", user.getSortableName());
342    
343            _userPopulationDAO.getUserPopulation(user.getIdentity().getPopulationId()).getLabel().toSAX(handler, "populationLabel");
344    
345            XMLUtils.endElement(handler, tagName);
346        }
347        else if (getLogger().isWarnEnabled())
348        {
349            getLogger().warn("Unable to sax null user", new Exception());
350        }
351    }
352    
353    /**
354     * Get the user identity from an XML configuration
355     * @param userConfiguration The configuration
356     * @return The user identity
357     * @throws ConfigurationException if an error occurs during value retrieving
358     */
359    public UserIdentity xml2userIdentity(Configuration userConfiguration) throws ConfigurationException
360    {
361        Element userElement = ConfigurationUtil.toElement(userConfiguration);
362        try
363        {
364            return xml2userIdentity(userElement);
365        }
366        catch (TransformerException e)
367        {
368            throw new ConfigurationException("An error occurred while parsing the configuration of an I/O data", e);
369        }
370    }
371    
372    /**
373     * Get the user identity from a DOM Node object
374     * @param userNode The node
375     * @return The user identity
376     * @throws TransformerException if an error occurs during value retrieving
377     */
378    public UserIdentity xml2userIdentity(Node userNode) throws TransformerException
379    {
380        String login = Objects.requireNonNull(XPathAPI.eval(userNode, "@login").str());
381        String population = Objects.requireNonNull(XPathAPI.eval(userNode, "@population").str());
382        return new UserIdentity(login, population);
383    }
384}