001/*
002 *  Copyright 2012 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.core.userpref;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Comparator;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.apache.cocoon.components.ContextHelper;
025import org.apache.cocoon.environment.Request;
026
027import org.ametys.core.ui.Callable;
028import org.ametys.core.util.I18nizableTextKeyComparator;
029import org.ametys.runtime.i18n.I18nizableText;
030import org.ametys.runtime.model.CategorizedElementDefinitionHelper;
031import org.ametys.runtime.model.DefinitionContext;
032import org.ametys.runtime.model.SimpleViewItemGroup;
033import org.ametys.runtime.model.View;
034import org.ametys.runtime.model.ViewElement;
035import org.ametys.runtime.model.ViewItem;
036import org.ametys.runtime.model.ViewItemGroup;
037import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
038import org.ametys.runtime.workspace.WorkspaceMatcher;
039
040/**
041 * Extension point holding all {@link UserPreference} definitions.
042 */
043public class UserPreferencesExtensionPoint extends AbstractThreadSafeComponentExtensionPoint<UserPreferenceProvider>
044{
045    /** Avalon Role */
046    public static final String ROLE = UserPreferencesExtensionPoint.class.getName();
047    
048    /**
049     * Get the declared user preference with the given name.
050     * @param contextVars The context variables including environment elements
051     * @param name The preference's name
052     * @return the user preference, or <code>null</code> if none has been found.
053     */
054    public UserPreference getUserPreference(Map<String, String> contextVars, String name)
055    {
056        return getUserPreferences(contextVars)
057                .stream()
058                .filter(userPreference -> name.equals(userPreference.getName()))
059                .findFirst()
060                .orElse(null);
061    }
062    
063    /**
064     * Get all the declared user preferences.
065     * @param contextVars The context variables including environment elements
066     * @return the user preferences (read-only collection).
067     */
068    public Collection<UserPreference> getUserPreferences(Map<String, String> contextVars)
069    {
070        Collection<UserPreference> userPreferences = new ArrayList<>();
071        
072        for (String extensionId : getExtensionsIds())
073        {
074            UserPreferenceProvider provider = getExtension(extensionId);
075            userPreferences.addAll(provider.getPreferences(contextVars));
076        }
077        
078        return userPreferences;
079    }
080    
081    /**
082     * Get the definition of user preferences
083     * @param excludePrivate true to exclude private preference from the view
084     * @return the view
085     */
086    @Callable(rights = Callable.NO_CHECK_REQUIRED)
087    public Map<String, Object> getUserPreferencesViewAsJson(boolean excludePrivate)
088    {
089        Request request = ContextHelper.getRequest(_context);
090        Map<String, String> contextVars = Map.of(UserPreferenceProvider.CONTEXT_VAR_WORKSPACE, (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_NAME));
091        View userPreferencesView = getUserPreferencesView(excludePrivate, contextVars);
092        DefinitionContext definitionContext = DefinitionContext.newInstance()
093                                                                   .withEdition(true);
094        return userPreferencesView.toJSON(definitionContext);
095    }
096    
097    /**
098     * Retrieves the view with all user preferences, classified by group and ordered.
099     * @param contextVars The context variables including environment elements
100     * @return the view with all user preferences, classified by group and ordered.
101     */
102    public View getUserPreferencesView(Map<String, String> contextVars)
103    {
104        return getUserPreferencesView(false, contextVars);
105    }
106    
107    /**
108     * Retrieves the view with all user preferences, classified by group and ordered.
109     * @param excludePrivate <code>true</code> to exclude private preferences from the view, <code>false</code> otherwise
110     * @param contextVars The context variables including environment elements
111     * @return the view with all user preferences, classified by group and ordered.
112     */
113    public View getUserPreferencesView(boolean excludePrivate, Map<String, String> contextVars)
114    {
115        Collection<UserPreference> preferences = getUserPreferences(contextVars);
116        
117        Comparator<I18nizableText> groupsComparator = new I18nizableTextKeyComparator();
118        Comparator<UserPreference> userPreferencesComparator = new UserPreferencesPositionComparator();
119        
120        return _buildView(preferences, groupsComparator, userPreferencesComparator, excludePrivate);
121    }
122    
123    private View _buildView(Collection<UserPreference> wrappers, Comparator<I18nizableText> groupsComparator, Comparator<UserPreference> userPreferencesComparator, boolean excludePrivate)
124    {
125        View view = new View();
126
127        Map<I18nizableText, Collection<UserPreference>> groups = _groupUserPreferences(wrappers);
128
129        Collection<I18nizableText> sortedGroups = CategorizedElementDefinitionHelper.sortItemsList(groups.keySet(), groupsComparator);
130        for (I18nizableText groupLabel : sortedGroups)
131        {
132            ViewItem groupViewItem = _buildGroupViewItem(groupLabel, groups.get(groupLabel), userPreferencesComparator, excludePrivate);
133            view.addViewItem(groupViewItem);
134        }
135        return view;
136    }
137
138    private Map<I18nizableText, Collection<UserPreference>> _groupUserPreferences(Collection<UserPreference> preferences)
139    {
140        Map<I18nizableText, Collection<UserPreference>> groups = new HashMap<>();
141
142        // Classify parameters by groups
143        for (UserPreference preference : preferences)
144        {
145            I18nizableText displayGroup = preference.getDisplayGroup();
146            Collection<UserPreference> group = groups.computeIfAbsent(displayGroup, __ -> new ArrayList<>());
147            group.add(preference);
148        }
149
150        return groups;
151    }
152
153    private ViewItem _buildGroupViewItem(I18nizableText groupLabel, Collection<UserPreference> preferences, Comparator<UserPreference> userPreferencesComparator, boolean excludePrivate) throws IllegalArgumentException
154    {
155        SimpleViewItemGroup groupViewItem = new SimpleViewItemGroup();
156        groupViewItem.setRole(ViewItemGroup.FIELDSET_ROLE);
157        groupViewItem.setLabel(groupLabel);
158
159        Collection<UserPreference> items = CategorizedElementDefinitionHelper.sortItemsList(preferences, userPreferencesComparator);
160        for (UserPreference item : items)
161        {
162            if (!item.isPrivate() || !excludePrivate)
163            {
164                ViewElement userPreferenceViewItem = new ViewElement();
165                userPreferenceViewItem.setDefinition(item);
166                groupViewItem.addViewItem(userPreferenceViewItem);
167            }
168        }
169
170        return groupViewItem;
171    }
172    
173    /**
174     * Compares user preferences on their position in the group
175     */
176    class UserPreferencesPositionComparator implements Comparator<UserPreference>
177    {
178        @Override
179        public int compare(UserPreference preference1, UserPreference preference2)
180        {
181            int positionComparison = CategorizedElementDefinitionHelper.comparePositions(preference1.getPosition(), preference2.getPosition());
182            if (positionComparison != 0)
183            {
184                return positionComparison;
185            }
186            else
187            {
188                return preference1.compareTo(preference2);
189            }
190        }
191    }
192}