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    /** Attribute path for user's image */
054    public static final String USER_CONTENT_IMAGE_PATH = "illustration/image";
055    private static final Comparator<Content> __COMPARATOR = Comparator.comparing(Content::getName);
056    private Set<String> _supportedData;
057    private UserDirectoryHelper _userDirectoryHelper;
058    private ContentTypesHelper _contentTypesHelper;
059    private Context _context;
060    
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        _userDirectoryHelper = (UserDirectoryHelper) manager.lookup(UserDirectoryHelper.ROLE);
064        _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
065    }
066    
067    public void contextualize(Context context) throws ContextException
068    {
069        _context = context;
070    }
071    
072    public int getPriority()
073    {
074        return 10000;
075    }
076
077    public void initialize() throws Exception
078    {
079        _supportedData = new HashSet<>();
080        
081        // Get the content types of a ud user
082        Map<String, Object> contentTypesList = _contentTypesHelper.getContentTypesList(Collections.singletonList("org.ametys.plugins.userdirectory.Content.user"),
083                true /* inherited */,
084                false /* checkRights */,
085                true /* includePrivate */,
086                false /* includeMixins */,
087                false /* includeAbstract*/);
088       
089        @SuppressWarnings("unchecked")
090        List<Map<String, Object>> contentTypes = (List<Map<String, Object>>) contentTypesList.get("contentTypes");
091        
092        // Get the model items names of all the model items that can be in a ud user
093        for (Map<String, Object> contentType : contentTypes)
094        {
095            Collection< ? extends ModelItem> modelItems = _contentTypesHelper.getModelItems(List.of(contentType.get("id"))
096                                                                             .toArray(String[]::new));
097            
098            _supportedData.addAll(modelItems.stream()
099                   .map(ModelItem::getName)
100                   .collect(Collectors.toSet()));
101        }
102    }
103    
104    public boolean supports(String element)
105    {
106        // Check if the element requested can be supported. Can return true but not have a value for a specific user.
107        return User.IMAGE_DATA_ID.equals(element) || _supportedData.contains(element);
108    }
109    
110    @Override
111    public boolean hasValue(User user, String dataId)
112    {
113        // Sort users by name in order to always retrieve the same value by dataId
114        List<Content> contentUsers = _getSortedContentUsers(user);
115        
116        String dataIdToCheck = User.IMAGE_DATA_ID.equals(dataId) ? USER_CONTENT_IMAGE_PATH : dataId;
117        
118        // Get the ud users and check if it has a value for the data requested
119        for (Content contentUser : contentUsers)
120        {
121            if (hasValue(contentUser, dataIdToCheck))
122            {
123                return true;
124            }
125        }
126        
127        return false;
128    }
129    
130    private boolean hasValue(Content contentUser , String dataId)
131    {
132        return contentUser != null && contentUser.hasValue(dataId);
133    }
134
135    public Object getValue(User user, String dataId)
136    {
137        // Sort users by name in order to always retrieve the same value by dataId
138        List<Content> contentUsers = _getSortedContentUsers(user);
139        
140        String dataIdToCheck = User.IMAGE_DATA_ID.equals(dataId) ? USER_CONTENT_IMAGE_PATH : dataId;
141
142        // Get the ud users and retrieve the value wanted if it exists
143        for (Content contentUser : contentUsers)
144        {
145            if (hasValue(contentUser, dataIdToCheck))
146            {
147                Object value = contentUser.getValue(dataIdToCheck);
148                
149                if (USER_CONTENT_IMAGE_PATH.equals(dataIdToCheck) && value != null && value instanceof File file)
150                {
151                    return new UserDirectoryImageAccessor(file, getLogger());
152                }
153                else
154                {
155                    return value;
156                }
157            }
158        }
159        
160        return null;
161    }
162    
163    private List<Content> _getSortedContentUsers(User user)
164    {
165        Request request = ContextHelper.getRequest(_context);
166        String lang = WebHelper.findLanguage(request);
167        
168        List<Content> userContents = new ArrayList<>(_userDirectoryHelper.getUserContents(user.getIdentity(), lang));
169        userContents.sort(__COMPARATOR);
170        return userContents;
171    }
172}