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.HashSet;
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 org.apache.avalon.framework.component.Component;
031import org.apache.avalon.framework.context.Context;
032import org.apache.avalon.framework.context.ContextException;
033import org.apache.avalon.framework.context.Contextualizable;
034import org.apache.avalon.framework.service.ServiceException;
035import org.apache.avalon.framework.service.ServiceManager;
036import org.apache.avalon.framework.service.Serviceable;
037import org.apache.cocoon.components.ContextHelper;
038import org.apache.cocoon.environment.Request;
039import org.apache.cocoon.xml.AttributesImpl;
040import org.apache.cocoon.xml.XMLUtils;
041import org.xml.sax.ContentHandler;
042import org.xml.sax.SAXException;
043
044import org.ametys.core.user.User;
045import org.ametys.core.user.UserIdentity;
046import org.ametys.core.user.UserManager;
047import org.ametys.core.user.directory.UserDirectoryFactory;
048import org.ametys.core.user.population.UserPopulationDAO;
049
050/**
051 * Simple user helper, for common function working on {@link User}
052 */
053public class UserHelper implements Component, Serviceable, Contextualizable
054{
055    /** The Avalon role */
056    public static final String ROLE = UserHelper.class.getName();
057    
058    private static final String __USER_CACHE_REQUEST_ATTR = UserHelper.class.getName() + "$userCache";
059    
060    /** The user population DAO */
061    private UserPopulationDAO _userPopulationDAO;
062
063    /** The user directory factory */
064    private UserDirectoryFactory _userDirectoryFactory;
065
066    private UserManager _userManager;
067
068    private Context _context;
069    
070    @Override
071    public void service(ServiceManager smanager) throws ServiceException
072    {
073        _userManager = (UserManager) smanager.lookup(UserManager.ROLE);
074        _userPopulationDAO = (UserPopulationDAO) smanager.lookup(UserPopulationDAO.ROLE);
075        _userDirectoryFactory = (UserDirectoryFactory) smanager.lookup(UserDirectoryFactory.ROLE);
076    }
077
078    @Override
079    public void contextualize(Context context) throws ContextException
080    {
081        _context = context;
082    }
083    
084    /**
085     * Get the user's full name handling request cache
086     * @param userIdentity The user identity
087     * @return user's full name or null if user does not exist
088     */
089    public String getUserFullName (UserIdentity userIdentity)
090    {
091        User user = getUser(userIdentity);
092        return user != null ? user.getFullName() : null;
093    }
094    
095    /**
096     * Get the user's sortable name handling request cache
097     * @param userIdentity The user identity
098     * @return user's full name or null if user does not exist
099     */
100    public String getUserSortableName (UserIdentity userIdentity)
101    {
102        User user = getUser(userIdentity);
103        return user != null ? user.getSortableName() : null;
104    }
105    
106    /**
107     * Populate a list of map, where each map representing an user.
108     * @param users The list of users
109     * @return The list of map.
110     */
111    public List<Map<String, Object>> userIdentities2json(Collection<UserIdentity> users)
112    {
113        return users.stream().map(identity -> getUser(identity)).filter(Objects::nonNull).map(user -> user2json(user)).collect(Collectors.toList());
114    }
115    
116    
117    /**
118     * Populate a list of map, where each map representing an user.
119     * @param users The list of users
120     * @return The list of map.
121     */
122    public List<Map<String, Object>> users2json(Collection<User> users)
123    {
124        return users2json(users, false);
125    }
126    
127    /**
128     * Populate a list of map, where each map representing an user.
129     * @param users The list of users
130     * @param full Set to <code>true</code> to get full information on user
131     * @return The list of map.
132     */
133    public List<Map<String, Object>> users2json(Collection<User> users, boolean full)
134    {
135        List<Map<String, Object>> userList = new ArrayList<>();
136        
137        Set<User> distinctUsers = new HashSet<>(users);
138        for (User user : distinctUsers)
139        {
140            userList.add(user2json(user, full));
141        }
142        
143        return userList;
144    }
145    
146    /**
147     * Get the JSON object representing a user
148     * @param userIdentity The user identity
149     * @return The user as JSON object
150     */
151    public Map<String, Object> user2json (UserIdentity userIdentity)
152    {
153        return user2json(userIdentity, false);
154    }
155    
156    /**
157     * Get the JSON object representing a user
158     * @param userIdentity The user identity
159     * @param full Set to <code>true</code> to get full information on user
160     * @return The user as JSON object
161     */
162    public Map<String, Object> user2json (UserIdentity userIdentity, boolean full)
163    {
164        User user = getUser(userIdentity);
165        if (user != null)
166        {
167            return user2json(user, full);
168        }
169        return Collections.EMPTY_MAP;
170    }
171    
172    /**
173     * Get the JSON object representing a user
174     * @param user The user
175     * @return The user as JSON object
176     */
177    public Map<String, Object> user2json(User user)
178    {
179        return _user2json(user, false);
180    }
181    
182    /**
183     * Get the JSON object representing a user
184     * @param user The user
185     * @param full Set to <code>true</code> to get full information on user
186     * @return The user as JSON object
187     */
188    public Map<String, Object> user2json(User user, boolean full)
189    {
190        return _user2json(user, full);
191    }
192    
193    private Map<String, Object> _user2json(User user, boolean full)
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     */
239    public User getUser (UserIdentity userIdentity)
240    {
241        if (userIdentity == null)
242        {
243            return null;
244        }
245        
246        Request request = _getRequest();
247        
248        if (request != null)
249        {
250            // Try to get user from cache if request is not null
251            if (request.getAttribute(__USER_CACHE_REQUEST_ATTR) == null)
252            {
253                request.setAttribute(__USER_CACHE_REQUEST_ATTR, new HashMap<UserIdentity, User>());
254            }
255            
256            @SuppressWarnings("unchecked")
257            Map<UserIdentity, User> userCache = (Map<UserIdentity, User>) request.getAttribute(__USER_CACHE_REQUEST_ATTR);
258            
259            User user = userCache.get(userIdentity);
260            if (user != null)
261            {
262                return user;
263            }
264
265            user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
266            if (user != null)
267            {
268                // Fill cache
269                userCache.put(userIdentity, user);
270            }
271            return user;
272        }
273        else
274        {
275            // Get user ouside of a request (no cache available)
276            return _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
277        }
278    }
279    
280    private Request _getRequest()
281    {
282        try
283        {
284            return ContextHelper.getRequest(_context);
285        }
286        catch (Exception e)
287        {
288            // There is no request
289            return null;
290        }  
291    }
292    
293    /**
294     * SAX an user
295     * @param user The user
296     * @param handler The content handler
297     * @throws SAXException If a SAX error occurs
298     */
299    public void saxUser(User user, ContentHandler handler) throws SAXException
300    {
301        saxUser(user, handler, "user");
302    }
303    
304    /**
305     * SAX an user
306     * @param user The user
307     * @param handler The content handler
308     * @param tagName The XML tag for saxed user
309     * @throws SAXException If a SAX error occurs
310     */
311    public void saxUser(User user, ContentHandler handler, String tagName) throws SAXException
312    {
313        AttributesImpl attr = new AttributesImpl();
314        attr.addCDATAAttribute("login", user.getIdentity().getLogin());
315        attr.addCDATAAttribute("population", user.getIdentity().getPopulationId());
316        
317        XMLUtils.startElement(handler, tagName, attr);
318        
319        XMLUtils.createElement(handler, "lastname", user.getLastName());
320        XMLUtils.createElement(handler, "firstname", user.getFirstName());
321        XMLUtils.createElement(handler, "email", user.getEmail());
322        
323        XMLUtils.createElement(handler, "fullname", user.getFullName());
324        XMLUtils.createElement(handler, "sortablename", user.getSortableName());
325
326        _userPopulationDAO.getUserPopulation(user.getIdentity().getPopulationId()).getLabel().toSAX(handler, "populationLabel");
327
328        XMLUtils.endElement(handler, tagName);
329    }
330}