001/* 002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and others. 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 * Contributors: 016 * Nuxeo - initial API and implementation 017 */ 018 019package org.nuxeo.ecm.user.registration.actions; 020 021import static org.jboss.seam.international.StatusMessage.Severity.ERROR; 022import static org.jboss.seam.international.StatusMessage.Severity.INFO; 023import static org.nuxeo.ecm.user.invite.UserInvitationService.ValidationMethod.EMAIL; 024import static org.nuxeo.ecm.user.registration.UserRegistrationService.CONFIGURATION_NAME; 025 026import java.io.Serializable; 027import java.util.HashMap; 028import java.util.Map; 029 030import javax.faces.application.FacesMessage; 031import javax.faces.component.UIComponent; 032import javax.faces.component.UIInput; 033import javax.faces.context.FacesContext; 034import javax.faces.validator.ValidatorException; 035import javax.mail.internet.AddressException; 036import javax.mail.internet.InternetAddress; 037 038import org.apache.commons.lang.StringUtils; 039import org.apache.commons.logging.Log; 040import org.apache.commons.logging.LogFactory; 041import org.jboss.seam.ScopeType; 042import org.jboss.seam.annotations.In; 043import org.jboss.seam.annotations.Name; 044import org.jboss.seam.annotations.Observer; 045import org.jboss.seam.annotations.Scope; 046import org.jboss.seam.core.Events; 047import org.jboss.seam.faces.FacesMessages; 048import org.nuxeo.ecm.core.api.CoreSession; 049import org.nuxeo.ecm.core.api.DocumentModel; 050import org.nuxeo.ecm.core.api.DocumentModelList; 051import org.nuxeo.ecm.core.api.DocumentRef; 052import org.nuxeo.ecm.core.api.IdRef; 053import org.nuxeo.ecm.core.api.NuxeoException; 054import org.nuxeo.ecm.core.api.NuxeoPrincipal; 055import org.nuxeo.ecm.platform.contentview.seam.ContentViewActions; 056import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 057import org.nuxeo.ecm.platform.ui.web.tag.fn.DocumentModelFunctions; 058import org.nuxeo.ecm.platform.ui.web.util.BaseURL; 059import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; 060import org.nuxeo.ecm.platform.usermanager.UserManager; 061import org.nuxeo.ecm.user.invite.UserRegistrationInfo; 062import org.nuxeo.ecm.user.registration.DocumentRegistrationInfo; 063import org.nuxeo.ecm.user.registration.UserRegistrationService; 064import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager; 065import org.nuxeo.ecm.webapp.helpers.EventNames; 066import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor; 067 068@Name("userRegistrationActions") 069@Scope(ScopeType.CONVERSATION) 070public class UserRegistrationActions implements Serializable { 071 072 private static final long serialVersionUID = 53468164827894L; 073 074 public static final String WIDGET_COMPONENT_EMAIL_ID = "nxw_user_request_email"; 075 076 private static Log log = LogFactory.getLog(UserRegistrationActions.class); 077 078 public static final String MULTIPLE_EMAILS_SEPARATOR = ";"; 079 080 public static final String REQUEST_DOCUMENT_LIST = "CURRENT_USER_REQUESTS"; 081 082 public static final String REQUESTS_DOCUMENT_LIST_CHANGED = "requestDocumentsChanged"; 083 084 protected UserRegistrationInfo userinfo = new UserRegistrationInfo(); 085 086 protected DocumentRegistrationInfo docinfo = new DocumentRegistrationInfo(); 087 088 protected String multipleEmails; 089 090 protected String comment; 091 092 protected boolean copyOwner = false; 093 094 @In(create = true) 095 protected transient NavigationContext navigationContext; 096 097 @In(create = true, required = false) 098 protected transient CoreSession documentManager; 099 100 @In(create = true, required = false) 101 protected transient FacesMessages facesMessages; 102 103 @In(create = true) 104 protected transient ResourcesAccessor resourcesAccessor; 105 106 @In(create = true) 107 protected transient DocumentsListsManager documentsListsManager; 108 109 @In(create = true) 110 protected transient ContentViewActions contentViewActions; 111 112 @In(create = true) 113 protected transient UserRegistrationService userRegistrationService; 114 115 @In(create = true) 116 protected transient UserManager userManager; 117 118 public UserRegistrationInfo getUserinfo() { 119 return userinfo; 120 } 121 122 public DocumentRegistrationInfo getDocinfo() { 123 return docinfo; 124 } 125 126 public String getComment() { 127 return comment; 128 } 129 130 public void setComment(String comment) { 131 this.comment = comment; 132 } 133 134 public boolean isCopyOwner() { 135 return copyOwner; 136 } 137 138 public void setCopyOwner(boolean copyOwner) { 139 this.copyOwner = copyOwner; 140 } 141 142 // Tweak to use same widgets between listing and forms 143 public UserRegistrationActions getData() { 144 return this; 145 } 146 147 public String getDocType() { 148 return getDocType(CONFIGURATION_NAME); 149 } 150 151 public String getDocType(String name) { 152 return userRegistrationService.getConfiguration(name).getRequestDocType(); 153 } 154 155 public String getValidationBaseUrl(String name) { 156 return BaseURL.getBaseURL() + userRegistrationService.getConfiguration(name).getValidationRelUrl(); 157 } 158 159 public String getEnterPasswordUrl(String name) { 160 return BaseURL.getBaseURL() + userRegistrationService.getConfiguration(name).getEnterPasswordUrl(); 161 } 162 163 public String getInvitationLayout(String name) { 164 return userRegistrationService.getConfiguration(name).getInvitationLayout(); 165 } 166 167 public String getListingLocalContentView(String name) { 168 return userRegistrationService.getConfiguration(name).getListingLocalContentView(); 169 } 170 171 public String getMultipleEmails() { 172 return multipleEmails; 173 } 174 175 public void setMultipleEmails(String multipleEmails) { 176 this.multipleEmails = multipleEmails; 177 } 178 179 public String getValidationBaseUrl() { 180 return getValidationBaseUrl(CONFIGURATION_NAME); 181 } 182 183 public String getEnterPasswordUrl() { 184 return getEnterPasswordUrl(CONFIGURATION_NAME); 185 } 186 187 public void acceptRegistrationRequest(DocumentModel request) { 188 try { 189 Map<String, Serializable> additionalInfo = new HashMap<String, Serializable>(); 190 additionalInfo.put("enterPasswordUrl", getEnterPasswordUrl()); 191 // Determine the document url to add it into the email 192 String docId = (String) request.getPropertyValue(DocumentRegistrationInfo.DOCUMENT_ID_FIELD); 193 DocumentRef docRef = new IdRef(docId); 194 DocumentModel doc = documentManager.getDocument(docRef); 195 String docUrl = DocumentModelFunctions.documentUrl("id", doc, null, null, false, BaseURL.getBaseURL()); 196 additionalInfo.put("docUrl", docUrl); 197 request.setPropertyValue("docinfo:creator", documentManager.getPrincipal().getName()); 198 documentManager.saveDocument(request); 199 200 userRegistrationService.acceptRegistrationRequest(request.getId(), additionalInfo); 201 202 // EventManager.raiseEventsOnDocumentChange(request); 203 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 204 } catch (NuxeoException e) { 205 facesMessages.add(ERROR, e.getMessage()); 206 } 207 } 208 209 public void rejectRegistrationRequest(DocumentModel request) { 210 try { 211 Map<String, Serializable> additionalInfo = new HashMap<String, Serializable>(); 212 additionalInfo.put("validationBaseURL", getValidationBaseUrl()); 213 userRegistrationService.rejectRegistrationRequest(request.getId(), additionalInfo); 214 // EventManager.raiseEventsOnDocumentChange(request); 215 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 216 } catch (NuxeoException e) { 217 facesMessages.add(ERROR, e.getMessage()); 218 } 219 } 220 221 public void submitUserRegistration(String configurationName) { 222 try { 223 docinfo.setDocumentId(navigationContext.getCurrentDocument().getId()); 224 docinfo.setDocumentTitle(navigationContext.getCurrentDocument().getTitle()); 225 doSubmitUserRegistration(configurationName); 226 resetPojos(); 227 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 228 } catch (NuxeoException e) { 229 facesMessages.add(ERROR, e.getMessage()); 230 } 231 } 232 233 public void submitMultipleUserRegistration(String configurationName) throws AddressException { 234 if (StringUtils.isBlank(multipleEmails)) { 235 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.registration.multiple.empty")); 236 return; 237 } 238 docinfo.setDocumentId(navigationContext.getCurrentDocument().getId()); 239 240 InternetAddress[] emails = splitAddresses(multipleEmails); 241 for (InternetAddress email : emails) { 242 userinfo.setLogin(email.getAddress()); 243 userinfo.setEmail(email.getAddress()); 244 245 log.debug("Request email: " + email + " with multiple invitation."); 246 doSubmitUserRegistration(configurationName); 247 } 248 resetPojos(); 249 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 250 } 251 252 protected InternetAddress[] splitAddresses(String emails) throws AddressException { 253 return StringUtils.isNotBlank(emails) ? InternetAddress.parse(emails.replace(MULTIPLE_EMAILS_SEPARATOR, ","), 254 false) : new InternetAddress[] {}; 255 } 256 257 public void validateMultipleUser(FacesContext context, UIComponent component, Object value) { 258 if (value instanceof String) { 259 try { 260 splitAddresses((String) value); 261 return; 262 } catch (AddressException e) { 263 // Nothing to do, error is handled after 264 } 265 } 266 267 FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, 268 "label.request.error.multiple.emails"), null); 269 270 // also add global message 271 context.addMessage(null, message); 272 throw new ValidatorException(message); 273 } 274 275 public boolean getCanValidate() { 276 boolean canDelete = !documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST); 277 for (DocumentModel doc : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 278 canDelete &= isDocumentValidable(doc); 279 } 280 return canDelete; 281 } 282 283 protected boolean isDocumentValidable(DocumentModel doc) { 284 return "approved".equals(doc.getCurrentLifeCycleState()); 285 } 286 287 public boolean getCanDelete() { 288 boolean canDelete = !documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST); 289 for (DocumentModel doc : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 290 canDelete &= isDocumentDeletable(doc); 291 } 292 return canDelete; 293 } 294 295 protected boolean isDocumentDeletable(DocumentModel doc) { 296 return !"validated".equals(doc.getCurrentLifeCycleState()); 297 } 298 299 public boolean getCanRevive() { 300 boolean canRevive = !documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST); 301 for (DocumentModel doc : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 302 canRevive &= isDocumentRevivable(doc); 303 } 304 return canRevive; 305 } 306 307 protected boolean isDocumentRevivable(DocumentModel doc) { 308 return "approved".equals(doc.getCurrentLifeCycleState()); 309 } 310 311 public void validateUserRegistration() { 312 if (!documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST)) { 313 try { 314 for (DocumentModel registration : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 315 userRegistrationService.validateRegistration(registration.getId(), 316 new HashMap<String, Serializable>()); 317 } 318 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 319 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.validate.request")); 320 documentsListsManager.resetWorkingList(REQUEST_DOCUMENT_LIST); 321 } catch (NuxeoException e) { 322 log.warn("Unable to validate registration: " + e.getMessage()); 323 log.info(e); 324 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.validate.request")); 325 } 326 } 327 } 328 329 public void reviveUserRegistration() { 330 if (!documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST)) { 331 try { 332 userRegistrationService.reviveRegistrationRequests(documentManager, 333 documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)); 334 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 335 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.revive.request")); 336 documentsListsManager.resetWorkingList(REQUEST_DOCUMENT_LIST); 337 } catch (NuxeoException e) { 338 log.warn("Unable to revive user: " + e.getMessage()); 339 log.info(e); 340 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.revive.request")); 341 } 342 } 343 } 344 345 public void deleteUserRegistration() { 346 if (!documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST)) { 347 try { 348 userRegistrationService.deleteRegistrationRequests(documentManager, 349 documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)); 350 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 351 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.delete.request")); 352 documentsListsManager.resetWorkingList(REQUEST_DOCUMENT_LIST); 353 } catch (NuxeoException e) { 354 log.warn("Unable to delete user request:" + e.getMessage()); 355 log.info(e); 356 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.delete.request")); 357 } 358 359 } 360 } 361 362 protected void doSubmitUserRegistration(String configurationName) { 363 if (StringUtils.isBlank(configurationName)) { 364 configurationName = CONFIGURATION_NAME; 365 } 366 367 try { 368 userRegistrationService.submitRegistrationRequest(configurationName, userinfo, docinfo, 369 getAdditionalsParameters(), EMAIL, false, documentManager.getPrincipal().getName()); 370 371 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.user.invited.success")); 372 } catch (NuxeoException e) { 373 log.info("Unable to register user: " + e.getMessage()); 374 log.debug(e, e); 375 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.invite"), userinfo.getLogin()); 376 facesMessages.add(ERROR, e.getMessage()); 377 } 378 } 379 380 protected Map<String, Serializable> getAdditionalsParameters() { 381 Map<String, Serializable> additionalsInfo = new HashMap<String, Serializable>(); 382 try { 383 additionalsInfo.put("docinfo:documentTitle", navigationContext.getCurrentDocument().getTitle()); 384 if (copyOwner) { 385 additionalsInfo.put("registration:copyTo", ((NuxeoPrincipal) documentManager.getPrincipal()).getEmail()); 386 } 387 additionalsInfo.put("registration:comment", comment); 388 } catch (NuxeoException e) { 389 // log it silently as it will break anything 390 log.debug(e, e); 391 } 392 return additionalsInfo; 393 } 394 395 @Observer({ EventNames.DOCUMENT_CHANGED }) 396 public void resetPojos() { 397 userinfo = new UserRegistrationInfo(); 398 docinfo = new DocumentRegistrationInfo(); 399 multipleEmails = ""; 400 copyOwner = false; 401 comment = ""; 402 } 403 404 @Observer({ REQUESTS_DOCUMENT_LIST_CHANGED }) 405 public void refreshContentViewCache() { 406 contentViewActions.refreshOnSeamEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 407 contentViewActions.resetPageProviderOnSeamEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 408 } 409 410 public void validateUsernameEmail(FacesContext context, UIComponent component, Object value) { 411 String email = (String) ((UIInput) component.findComponent(WIDGET_COMPONENT_EMAIL_ID)).getLocalValue(); 412 if (email != null) { 413 if (value == null) { 414 value = email; 415 } 416 NuxeoPrincipal principal = userManager.getPrincipal((String) value); 417 if (principal != null) { 418 if (!email.equals(principal.getEmail())) { 419 FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate( 420 context, "label.unicity.usernamepwd"), null); 421 context.addMessage(null, message); 422 throw new ValidatorException(message); 423 } 424 return; 425 } 426 Map<String, Serializable> filter = new HashMap<>(); 427 filter.put("email", email); 428 DocumentModelList users = userManager.searchUsers(filter, filter.keySet()); 429 if (users.size() != 0) { 430 FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, 431 "label.unicity.sameemail", users.get(0).getPropertyValue("user:username")), null); 432 context.addMessage(null, message); 433 throw new ValidatorException(message); 434 } 435 } 436 } 437}