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