001/*
002 *  Copyright 2024 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.userdirectory.userdataprovider;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.Comparator;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026import java.util.stream.Collectors;
027
028import org.apache.avalon.framework.activity.Initializable;
029import org.apache.avalon.framework.context.Context;
030import org.apache.avalon.framework.context.ContextException;
031import org.apache.avalon.framework.context.Contextualizable;
032import org.apache.avalon.framework.service.ServiceException;
033import org.apache.avalon.framework.service.ServiceManager;
034import org.apache.avalon.framework.service.Serviceable;
035import org.apache.cocoon.components.ContextHelper;
036import org.apache.cocoon.environment.Request;
037
038import org.ametys.cms.contenttype.ContentTypesHelper;
039import org.ametys.cms.data.File;
040import org.ametys.cms.repository.Content;
041import org.ametys.core.user.User;
042import org.ametys.core.user.dataprovider.UserDataProvider;
043import org.ametys.plugins.userdirectory.UserDirectoryHelper;
044import org.ametys.runtime.model.ModelItem;
045import org.ametys.runtime.plugin.component.AbstractLogEnabled;
046import org.ametys.web.WebHelper;
047
048/**
049 * A {@link UserDataProvider} that finds data from the ContentUser associated to the User or UserIdentity given
050 */
051public class ContentUserDataProvider extends AbstractLogEnabled implements UserDataProvider, Serviceable, Initializable, Contextualizable
052{
053    private static final Comparator<Content> __COMPARATOR = Comparator.comparing(Content::getName);
054    private Set<String> _supportedData;
055    private UserDirectoryHelper _userDirectoryHelper;
056    private ContentTypesHelper _contentTypesHelper;
057    private Context _context;
058    
059    public void service(ServiceManager manager) throws ServiceException
060    {
061        _userDirectoryHelper = (UserDirectoryHelper) manager.lookup(UserDirectoryHelper.ROLE);
062        _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
063    }
064    
065    public void contextualize(Context context) throws ContextException
066    {
067        _context = context;
068    }
069    
070    public int getPriority()
071    {
072        return 10000;
073    }
074
075    public void initialize() throws Exception
076    {
077        _supportedData = new HashSet<>();
078        
079        // Get the content types of a ud user
080        Map<String, Object> contentTypesList = _contentTypesHelper.getContentTypesList(Collections.singletonList("org.ametys.plugins.userdirectory.Content.user"),
081                true /* inherited */,
082                false /* checkRights */,
083                true /* includePrivate */,
084                false /* includeMixins */,
085                false /* includeAbstract*/);
086       
087        @SuppressWarnings("unchecked")
088        List<Map<String, Object>> contentTypes = (List<Map<String, Object>>) contentTypesList.get("contentTypes");
089        
090        // Get the model items names of all the model items that can be in a ud user
091        for (Map<String, Object> contentType : contentTypes)
092        {
093            Collection< ? extends ModelItem> modelItems = _contentTypesHelper.getModelItems(List.of(contentType.get("id"))
094                                                                             .toArray(String[]::new));
095            
096            _supportedData.addAll(modelItems.stream()
097                   .map(ModelItem::getName)
098                   .collect(Collectors.toSet()));
099        }
100    }
101    
102    public boolean supports(String element)
103    {
104        // Check if the element requested can be supported. Can return true but not have a value for a specific user.
105        return _supportedData.contains(element);
106    }
107    
108    @Override
109    public boolean hasValue(User user, String dataId)
110    {
111        // Sort users by name in order to always retrieve the same value by dataId
112        List<Content> contentUsers = _getSortedContentUsers(user);
113        
114        // Get the ud users and check if it has a value for the data requested
115        for (Content contentUser : contentUsers)
116        {
117            if (hasValue(contentUser, dataId))
118            {
119                return true;
120            }
121        }
122        
123        return false;
124    }
125    
126    private boolean hasValue(Content contentUser , String dataId)
127    {
128        return contentUser != null && contentUser.hasValue(dataId);
129    }
130
131    public Object getValue(User user, String dataId)
132    {
133        // Sort users by name in order to always retrieve the same value by dataId
134        List<Content> contentUsers = _getSortedContentUsers(user);
135        
136        // Get the ud users and retrieve the value wanted if it exists
137        for (Content contentUser : contentUsers)
138        {
139            if (hasValue(contentUser, dataId))
140            {
141                Object value = contentUser.getValue(dataId);
142                
143                if (User.IMAGE_DATA_ID.equals(dataId) && value != null && value instanceof File file)
144                {
145                    return new UserDirectoryImageAccessor(file, getLogger());
146                }
147                else
148                {
149                    return value;
150                }
151            }
152        }
153        
154        return null;
155    }
156    
157    private List<Content> _getSortedContentUsers(User user)
158    {
159        Request request = ContextHelper.getRequest(_context);
160        String lang = WebHelper.findLanguage(request);
161        
162        List<Content> userContents = new ArrayList<>(_userDirectoryHelper.getUserContents(user.getIdentity(), lang));
163        userContents.sort(__COMPARATOR);
164        return userContents;
165    }
166}