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.plugins.core.userpref;
017
018import java.text.DateFormat;
019import java.text.ParseException;
020import java.text.SimpleDateFormat;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.Date;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.Map;
027import java.util.Set;
028
029import org.apache.avalon.framework.parameters.Parameters;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.cocoon.environment.ObjectModelHelper;
033import org.apache.cocoon.environment.Redirector;
034import org.apache.cocoon.environment.Request;
035import org.apache.cocoon.environment.SourceResolver;
036import org.apache.commons.lang.StringUtils;
037
038import org.ametys.core.user.UserIdentity;
039import org.ametys.core.userpref.UserPreference;
040import org.ametys.core.userpref.UserPreferenceProvider;
041import org.ametys.core.userpref.UserPreferencesErrors;
042import org.ametys.core.userpref.UserPreferencesException;
043import org.ametys.core.userpref.UserPreferencesExtensionPoint;
044import org.ametys.core.userpref.UserPreferencesManager;
045import org.ametys.core.util.cocoon.AbstractCurrentUserProviderServiceableAction;
046import org.ametys.runtime.i18n.I18nizableText;
047import org.ametys.runtime.parameter.ParameterHelper;
048import org.ametys.runtime.parameter.ParameterHelper.ParameterType;
049import org.ametys.runtime.workspace.WorkspaceMatcher;
050
051/**
052 * Action which saves the user preferences values into the database.
053 */
054public class SetUserPreferencesAction extends AbstractCurrentUserProviderServiceableAction
055{
056    
057    /** The input date format. */
058    protected static final Set<DateFormat> _INPUT_DATE_FORMATS = new HashSet<>();
059    static
060    {
061        _INPUT_DATE_FORMATS.add(new SimpleDateFormat("yyyy-MM-dd"));
062        _INPUT_DATE_FORMATS.add(new SimpleDateFormat("dd/MM/yyyy"));
063    }
064    
065    /** The user preferences extension point. */
066    protected UserPreferencesExtensionPoint _userPrefEP;
067    
068    /** The user preferences manager. */
069    protected UserPreferencesManager _userPrefManager;
070    
071    @Override
072    public void service(ServiceManager serviceManager) throws ServiceException
073    {
074        super.service(serviceManager);
075    }
076    
077    @SuppressWarnings("unchecked")
078    @Override
079    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
080    {
081        if (_userPrefEP == null)
082        {
083            _userPrefEP = (UserPreferencesExtensionPoint) manager.lookup(UserPreferencesExtensionPoint.ROLE);
084            _userPrefManager = (UserPreferencesManager) manager.lookup(UserPreferencesManager.ROLE);
085        }
086
087        Request request = ObjectModelHelper.getRequest(objectModel);
088        Map<String, Object> parentContext = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
089        
090        String storageContext = getStorageContext(request, parameters);
091        UserIdentity user = getUser(request, parameters);
092        
093        String submit = request.getParameter("submit");
094        
095        Map<String, String> results = new HashMap<>();
096        
097        if ("true".equals(submit))
098        {
099            Collection<String> preferenceIds = getPreferenceIds(request, parentContext);
100            Map<String, String> contextVars = getContextVars(request);
101            
102            results = setUserPreferences(request, storageContext, contextVars, user, preferenceIds);
103        }
104        
105        return results;
106    }
107    
108    /**
109     * Get the user
110     * @param request The request
111     * @param parameters The sitemap parameters
112     * @return the user
113     */
114    protected UserIdentity getUser(Request request, Parameters parameters)
115    {
116        String username = parameters.getParameter("username", "");
117        String userPopulation = parameters.getParameter("userpopulation", "");
118        UserIdentity user;
119        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(userPopulation))
120        {
121            user = _getCurrentUser();
122        }
123        else
124        {
125            user = new UserIdentity(username, userPopulation);
126        }
127        
128        return user;
129    }
130    
131    /**
132     * Get the context of user's preference storage
133     * @param request The request
134     * @param parameters The sitemap parameters
135     * @return the storage context
136     */
137    protected String getStorageContext(Request request, Parameters parameters)
138    {
139        return parameters.getParameter("prefContext", request.getParameter("prefContext"));
140    }
141    
142    /**
143     * Set user preferences.
144     * @param request the request.
145     * @param storageContext the preferences storage context.
146     * @param contextVars the preferences context map.
147     * @param user the user.
148     * @return the results.
149     * @throws UserPreferencesException if an error occurred
150     */
151    protected Map<String, String> setUserPreferences(Request request, String storageContext, Map<String, String> contextVars, UserIdentity user) throws UserPreferencesException
152    {
153        return setUserPreferences(request, storageContext, contextVars, user, Collections.<String>emptySet());
154    }
155    
156    /**
157     * Set user preferences.
158     * @param request the request.
159     * @param storageContext the preferences context.
160     * @param contextVars the preferences context map.
161     * @param user the user.
162     * @param preferenceIds a collection of the IDs of preferences to set.
163     * @return the results.
164     * @throws UserPreferencesException if an error occurred
165     */
166    protected Map<String, String> setUserPreferences(Request request, String storageContext, Map<String, String> contextVars, UserIdentity user, Collection<String> preferenceIds) throws UserPreferencesException
167    {
168        Map<String, String> results = new HashMap<>();
169        
170        results.put("status", "error");
171        
172        Map<String, String> values = _userPrefManager.getUnTypedUserPrefs(user, storageContext, contextVars);
173        
174        UserPreferencesErrors errors = new UserPreferencesErrors();
175        
176        // Override the old values with the new ones, but keep old values when new preferences are not in the request.
177        values.putAll(_getValues(request, contextVars, user, preferenceIds, errors));
178        
179        // Validate the user preferences, filling in potential errors.
180        _userPrefEP.validatePreferences(contextVars, values, errors);
181        
182        if (!errors.hasErrors())
183        {
184            _userPrefManager.setUserPreferences(user, storageContext, contextVars, values);
185            results.put("status", "success");
186        }
187        else
188        {
189            request.setAttribute("user-prefs-errors", errors);
190        }
191        
192        return results;
193    }
194    
195    /**
196     * Get the preferences values from the request.
197     * @param request the request.
198     * @param contextVars The context vars
199     * @param user the user.
200     * @param preferenceIds a collection of the IDs of preferences to set.
201     * @param errors the errors object to fill in.
202     * @return the user preferences values as a Map.
203     */
204    protected Map<String, String> _getValues(Request request, Map<String, String> contextVars, UserIdentity user, Collection<String> preferenceIds, UserPreferencesErrors errors)
205    {
206        Map<String, String> preferences = new HashMap<>();
207        
208        for (UserPreference preference : _userPrefEP.getUserPreferences(contextVars).values())
209        {
210            String id = preference.getId();
211            if (preference.getType() == ParameterType.DATE)
212            {
213                String value = request.getParameter(id);
214                if (value != null)
215                {
216                    if (StringUtils.isBlank(value))
217                    {
218                        preferences.put(id, value);
219                    }
220                    else
221                    {
222                        Date date = _parseDate(value);
223                        if (date != null)
224                        {
225                            preferences.put(id, ParameterHelper.valueToString(date));
226                        }
227                    }
228                }
229            }
230            else if (preference.getType() == ParameterType.PASSWORD)
231            {
232                // Password: if no new value is provided, keep the old value.
233                String value = request.getParameter(id);
234                if (StringUtils.isNotBlank(value))
235                {
236                    // Check if the confirmation match.
237                    String confirmationValue = request.getParameter(id + "-confirmation");
238                    if (!value.equals(confirmationValue))
239                    {
240                        errors.addError(id, new I18nizableText("plugin.core", "PLUGINS_CORE_UI_USER_PREFERENCES_PWD_CONFIRMATION_DOESNT_MATCH"));
241                    }
242                    preferences.put(id, value);
243                }
244            }
245            else if (preference.getType() == ParameterType.BOOLEAN && preferenceIds.contains(id))
246            {
247                // Boolean value: if the preference is set if in the request, it's true, if it's not, it's false.
248                // So, check that the preference was wanted (present in preferenceIds).
249                String value = request.getParameter(id);
250                String valueStr = String.valueOf("true".equals(value));
251                preferences.put(id, valueStr);
252            }
253            else
254            {
255                String[] values = request.getParameterValues(id);
256                if (values != null)
257                {
258                    String valuesStr = StringUtils.join(values, ',');
259                    preferences.put(id, valuesStr);
260                }
261            }
262        }
263        
264        return preferences;
265    }
266    
267    /**
268     * Parse a user-submitted date.
269     * @param value the date value as a String.
270     * @return the Date.
271     */
272    protected Date _parseDate(String value)
273    {
274        Date date = null;
275        for (DateFormat format : _INPUT_DATE_FORMATS)
276        {
277            try
278            {
279                date = format.parse(value);
280                return date;
281            }
282            catch (ParseException e)
283            {
284                // Ignore.
285            }
286        }
287        return date;
288    }
289    
290    /**
291     * Get the preferences context variables.
292     * @param request the request.
293     * @return the preferences context as a Map.
294     */
295    protected Map<String, String> getContextVars(Request request)
296    {
297        Map<String, String> contextVars = new HashMap<>();
298        contextVars.put(UserPreferenceProvider.CONTEXT_VAR_WORKSPACE, (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_NAME));
299        return contextVars;
300    }
301    
302    /**
303     * Get the preferences to set.
304     * @param request the request.
305     * @param parentContext the parent context.
306     * @return a collection of the IDs of preferences to set.
307     */
308    protected Collection<String> getPreferenceIds(Request request, Map<String, Object> parentContext)
309    {
310        return Collections.emptySet();
311    }
312    
313}