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