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