001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 */
016 package org.kuali.rice.kew.preferences.service.impl;
017
018 import java.util.ArrayList;
019 import java.util.Collection;
020 import java.util.HashMap;
021 import java.util.Map;
022 import java.util.Map.Entry;
023
024 import org.apache.commons.lang.StringUtils;
025 import org.kuali.rice.core.api.config.property.ConfigContext;
026 import org.kuali.rice.core.api.config.property.ConfigurationService;
027 import org.kuali.rice.kew.api.KewApiConstants;
028 import org.kuali.rice.kew.api.preferences.Preferences;
029 import org.kuali.rice.kew.api.preferences.PreferencesService;
030 import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
031 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
032 import org.kuali.rice.kew.service.KEWServiceLocator;
033 import org.kuali.rice.kew.useroptions.UserOptions;
034 import org.kuali.rice.kew.useroptions.UserOptionsService;
035 import org.kuali.rice.krad.service.KRADServiceLocator;
036
037
038 /**
039 * An implementation of the {@link PreferencesService}.
040 *
041 * @author Kuali Rice Team (rice.collab@kuali.org)
042 */
043 public class PreferencesServiceImpl implements PreferencesService {
044
045 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PreferencesServiceImpl.class);
046
047 private static Map<String, String> USER_OPTION_KEY_DEFAULT_MAP;
048
049 static {
050 USER_OPTION_KEY_DEFAULT_MAP = new HashMap<String, String>();
051 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_APPROVED, "userOptions.default.color");
052 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_CANCELED, "userOptions.default.color");
053 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_DISAPPROVE_CANCEL, "userOptions.default.color");
054 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_DISAPPROVED, "userOptions.default.color");
055 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_ENROUTE, "userOptions.default.color");
056 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_EXCEPTION, "userOptions.default.color");
057 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_FINAL, "userOptions.default.color");
058 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_INITIATED, "userOptions.default.color");
059 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_PROCESSED, "userOptions.default.color");
060 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.COLOR_SAVED, "userOptions.default.color");
061 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.EMAIL_NOTIFICATION, "userOptions.default.email");
062 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.NOTIFY_PRIMARY_DELEGATION, "userOptions.default.notifyPrimary");
063 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.NOTIFY_SECONDARY_DELEGATION, "userOptions.default.notifySecondary");
064 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.OPEN_NEW_WINDOW, "userOptions.default.openNewWindow");
065 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.PAGE_SIZE, "userOptions.default.actionListSize");
066 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.REFRESH_RATE, "userOptions.default.refreshRate");
067 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_ACTION_REQUESTED, "userOptions.default.showActionRequired");
068 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_DATE_CREATED, "userOptions.default.showDateCreated");
069 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_DOC_TYPE, "userOptions.default.showDocumentType");
070 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_DOCUMENT_STATUS, "userOptions.default.showDocumentStatus");
071 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_INITIATOR, "showInitiator");
072 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_DELEGATOR, "userOptions.default.showDelegator");
073 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_DOC_TITLE, "userOptions.default.showTitle");
074 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_GROUP_REQUEST, "userOptions.default.showWorkgroupRequest");
075 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_CLEAR_FYI, "userOptions.default.showClearFYI");
076 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.DELEGATOR_FILTER, "userOptions.default.delegatorFilterOnActionList");
077 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.PRIMARY_DELEGATE_FILTER, "userOptions.default.primaryDelegatorFilterOnActionList");
078 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_DATE_APPROVED, "userOptions.default.showLastApprovedDate");
079 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.SHOW_CURRENT_NODE, "userOptions.default.showCurrentNode");
080 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.USE_OUT_BOX, KewApiConstants.USER_OPTIONS_DEFAULT_USE_OUTBOX_PARAM);
081 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.NOTIFY_ACKNOWLEDGE, "userOptions.default.notifyAcknowledge");
082 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.NOTIFY_APPROVE, "userOptions.default.notifyApprove");
083 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.NOTIFY_COMPLETE, "userOptions.default.notifyComplete");
084 USER_OPTION_KEY_DEFAULT_MAP.put(Preferences.KEYS.NOTIFY_FYI, "userOptions.default.notifyFYI");
085 }
086
087
088 public Preferences getPreferences(String principalId) {
089 if ( LOG.isDebugEnabled() ) {
090 LOG.debug("start preferences fetch user " + principalId);
091 }
092 Collection<UserOptions> options = getUserOptionService().findByWorkflowUser(principalId);
093 Map<String,UserOptions> optionMap = new HashMap<String, UserOptions>();
094 Map<String,String> optionValueMap = new HashMap<String, String>();
095 Map<String, String> documentTypeNotificationPreferences = new HashMap<String, String>();
096 for ( UserOptions option : options ) {
097 if(option.getOptionId().endsWith(KewApiConstants.DOCUMENT_TYPE_NOTIFICATION_PREFERENCE_SUFFIX)) {
098 String preferenceName = option.getOptionId();
099 preferenceName = StringUtils.substringBeforeLast(preferenceName, KewApiConstants.DOCUMENT_TYPE_NOTIFICATION_PREFERENCE_SUFFIX);
100 documentTypeNotificationPreferences.put(preferenceName, option.getOptionVal());
101 } else {
102 optionMap.put(option.getOptionId(), option);
103 }
104 }
105
106 ConfigurationService kcs = KRADServiceLocator.getKualiConfigurationService();
107
108 boolean isSaveRequired = false;
109
110 for (Map.Entry<String, String> entry : USER_OPTION_KEY_DEFAULT_MAP.entrySet()) {
111 String optionKey = entry.getKey();
112 String defaultValue = kcs.getPropertyValueAsString(entry.getValue());
113 if (LOG.isDebugEnabled()) {
114 LOG.debug("start fetch option " + optionKey + " user " + principalId);
115 }
116
117 UserOptions option = optionMap.get(optionKey);
118 if (option == null) {
119 if (LOG.isDebugEnabled()) {
120 LOG.debug("User option '"
121 + optionKey
122 + "' on user "
123 + principalId
124 + " has no stored value. Preferences will require save.");
125 }
126 option = new UserOptions();
127 option.setWorkflowId(principalId);
128 option.setOptionId(optionKey);
129 option.setOptionVal(defaultValue);
130 optionMap.put(optionKey, option); // just in case referenced a second time
131
132 if (!isSaveRequired) {
133 if (optionKey.equals(Preferences.KEYS.USE_OUT_BOX) && !ConfigContext.getCurrentContextConfig().getOutBoxOn()) {
134 // don't mark as needing save
135 } else {
136 isSaveRequired = true;
137 }
138 }
139 }
140 if (LOG.isDebugEnabled()) {
141 LOG.debug("End fetch option " + optionKey + " user " + principalId);
142 }
143
144 optionValueMap.put(optionKey, option.getOptionVal());
145 }
146
147 // TODO: JLR - I'm not sure why this isSaveRequired logic is necessary -- couldn't we do something like the following?
148 // if (isSaveRequired)
149 // getUserOptionService().save(principalId, optionValueMap);
150
151 return Preferences.Builder.create(optionValueMap, documentTypeNotificationPreferences, isSaveRequired).build();
152 }
153
154 public void savePreferences(String principalId, Preferences preferences) {
155 // NOTE: this previously displayed the principalName. Now it's just the id
156 if ( LOG.isDebugEnabled() ) {
157 LOG.debug("saving preferences user " + principalId);
158 }
159
160 validate(preferences);
161 Map<String,String> optionsMap = new HashMap<String,String>(50);
162
163 optionsMap.put(Preferences.KEYS.COLOR_DISAPPROVE_CANCEL, preferences.getColorDisapproveCancel());
164 optionsMap.put(Preferences.KEYS.COLOR_DISAPPROVED, preferences.getColorDisapproved());
165 optionsMap.put(Preferences.KEYS.COLOR_APPROVED, preferences.getColorApproved());
166 optionsMap.put(Preferences.KEYS.COLOR_CANCELED, preferences.getColorCanceled());
167 optionsMap.put(Preferences.KEYS.COLOR_SAVED, preferences.getColorSaved());
168 optionsMap.put(Preferences.KEYS.COLOR_ENROUTE, preferences.getColorEnroute());
169 optionsMap.put(Preferences.KEYS.COLOR_PROCESSED, preferences.getColorProcessed());
170 optionsMap.put(Preferences.KEYS.COLOR_INITIATED, preferences.getColorInitiated());
171 optionsMap.put(Preferences.KEYS.COLOR_FINAL, preferences.getColorFinal());
172 optionsMap.put(Preferences.KEYS.COLOR_EXCEPTION, preferences.getColorException());
173 optionsMap.put(Preferences.KEYS.REFRESH_RATE, preferences.getRefreshRate().trim());
174 optionsMap.put(Preferences.KEYS.OPEN_NEW_WINDOW, preferences.getOpenNewWindow());
175 optionsMap.put(Preferences.KEYS.SHOW_DOC_TYPE, preferences.getShowDocType());
176 optionsMap.put(Preferences.KEYS.SHOW_DOC_TITLE, preferences.getShowDocTitle());
177 optionsMap.put(Preferences.KEYS.SHOW_ACTION_REQUESTED, preferences.getShowActionRequested());
178 optionsMap.put(Preferences.KEYS.SHOW_INITIATOR, preferences.getShowInitiator());
179 optionsMap.put(Preferences.KEYS.SHOW_DELEGATOR, preferences.getShowDelegator());
180 optionsMap.put(Preferences.KEYS.SHOW_DATE_CREATED, preferences.getShowDateCreated());
181 optionsMap.put(Preferences.KEYS.SHOW_DOCUMENT_STATUS, preferences.getShowDocumentStatus());
182 optionsMap.put(Preferences.KEYS.SHOW_APP_DOC_STATUS, preferences.getShowAppDocStatus());
183 optionsMap.put(Preferences.KEYS.SHOW_GROUP_REQUEST, preferences.getShowWorkgroupRequest());
184 optionsMap.put(Preferences.KEYS.SHOW_CLEAR_FYI, preferences.getShowClearFyi());
185 optionsMap.put(Preferences.KEYS.PAGE_SIZE, preferences.getPageSize().trim());
186 optionsMap.put(Preferences.KEYS.EMAIL_NOTIFICATION, preferences.getEmailNotification());
187 optionsMap.put(Preferences.KEYS.NOTIFY_PRIMARY_DELEGATION, preferences.getNotifyPrimaryDelegation());
188 optionsMap.put(Preferences.KEYS.NOTIFY_SECONDARY_DELEGATION, preferences.getNotifySecondaryDelegation());
189 optionsMap.put(Preferences.KEYS.DELEGATOR_FILTER, preferences.getDelegatorFilter());
190 optionsMap.put(Preferences.KEYS.PRIMARY_DELEGATE_FILTER, preferences.getPrimaryDelegateFilter());
191 optionsMap.put(Preferences.KEYS.SHOW_DATE_APPROVED, preferences.getShowDateApproved());
192 optionsMap.put(Preferences.KEYS.SHOW_CURRENT_NODE, preferences.getShowCurrentNode());
193 optionsMap.put(Preferences.KEYS.NOTIFY_ACKNOWLEDGE, preferences.getNotifyAcknowledge());
194 optionsMap.put(Preferences.KEYS.NOTIFY_APPROVE, preferences.getNotifyApprove());
195 optionsMap.put(Preferences.KEYS.NOTIFY_COMPLETE, preferences.getNotifyComplete());
196 optionsMap.put(Preferences.KEYS.NOTIFY_FYI, preferences.getNotifyFYI());
197 if (ConfigContext.getCurrentContextConfig().getOutBoxOn()) {
198 optionsMap.put(Preferences.KEYS.USE_OUT_BOX, preferences.getUseOutbox());
199 }
200 for(Entry<String, String> documentTypePreference : preferences.getDocumentTypeNotificationPreferences().entrySet()) {
201 optionsMap.put(documentTypePreference.getKey() + KewApiConstants.DOCUMENT_TYPE_NOTIFICATION_PREFERENCE_SUFFIX, documentTypePreference.getValue());
202 }
203 getUserOptionService().save(principalId, optionsMap);
204
205 // Find which document type notification preferences have been deleted
206 // and remove them from the database
207 Preferences storedPreferences = this.getPreferences(principalId);
208 for(Entry<String, String> storedEntry : storedPreferences.getDocumentTypeNotificationPreferences().entrySet()) {
209 if(preferences.getDocumentTypeNotificationPreference(storedEntry.getKey()) == null) {
210 getUserOptionService().deleteUserOptions(getUserOptionService().findByOptionId(storedEntry.getKey() + KewApiConstants.DOCUMENT_TYPE_NOTIFICATION_PREFERENCE_SUFFIX, principalId));
211 }
212 }
213 if ( LOG.isDebugEnabled() ) {
214 LOG.debug("saved preferences user " + principalId);
215 }
216 }
217
218 private void validate(Preferences preferences) {
219 LOG.debug("validating preferences");
220
221 Collection errors = new ArrayList();
222 try {
223 new Integer(preferences.getRefreshRate().trim());
224 } catch (NumberFormatException e) {
225 errors.add(new WorkflowServiceErrorImpl("ActionList Refresh Rate must be in whole " +
226 "minutes", Preferences.KEYS.ERR_KEY_REFRESH_RATE_WHOLE_NUM));
227 } catch (NullPointerException e1) {
228 errors.add(new WorkflowServiceErrorImpl("ActionList Refresh Rate must be in whole " +
229 "minutes", Preferences.KEYS.ERR_KEY_REFRESH_RATE_WHOLE_NUM));
230 }
231
232 try {
233 if(new Integer(preferences.getPageSize().trim()) == 0){
234 errors.add(new WorkflowServiceErrorImpl("ActionList Page Size must be non-zero ",
235 Preferences.KEYS.ERR_KEY_ACTION_LIST_PAGE_SIZE_WHOLE_NUM));
236 }
237 } catch (NumberFormatException e) {
238 errors.add(new WorkflowServiceErrorImpl("ActionList Page Size must be in whole " +
239 "minutes", Preferences.KEYS.ERR_KEY_ACTION_LIST_PAGE_SIZE_WHOLE_NUM));
240 } catch (NullPointerException e1) {
241 errors.add(new WorkflowServiceErrorImpl("ActionList Page Size must be in whole " +
242 "minutes", Preferences.KEYS.ERR_KEY_ACTION_LIST_PAGE_SIZE_WHOLE_NUM));
243 }
244
245 LOG.debug("end validating preferences");
246 if (! errors.isEmpty()) {
247 throw new WorkflowServiceErrorException("Preference Validation Error", errors);
248 }
249 }
250
251 public UserOptionsService getUserOptionService() {
252 return (UserOptionsService) KEWServiceLocator.getService(
253 KEWServiceLocator.USER_OPTIONS_SRV);
254 }
255
256 private final class UserOptionsWrapper {
257
258 private final UserOptions userOptions;
259 private final boolean isSaveRequired;
260
261 public UserOptionsWrapper(UserOptions userOptions, boolean isSaveRequired) {
262 this.userOptions = userOptions;
263 this.isSaveRequired = isSaveRequired;
264 }
265
266 public UserOptions getUserOptions() {
267 return userOptions;
268 }
269
270 public boolean isSaveRequired() {
271 return isSaveRequired;
272 }
273 }
274 }
275
276