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 request.setPropertyValue("docinfo:creator", documentManager.getPrincipal().getName()); 195 documentManager.saveDocument(request); 196 197 userRegistrationService.acceptRegistrationRequest(request.getId(), additionalInfo); 198 199 // EventManager.raiseEventsOnDocumentChange(request); 200 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 201 } catch (NuxeoException e) { 202 facesMessages.add(ERROR, e.getMessage()); 203 } 204 } 205 206 public void rejectRegistrationRequest(DocumentModel request) { 207 try { 208 Map<String, Serializable> additionalInfo = new HashMap<String, Serializable>(); 209 additionalInfo.put("validationBaseURL", getValidationBaseUrl()); 210 userRegistrationService.rejectRegistrationRequest(request.getId(), additionalInfo); 211 // EventManager.raiseEventsOnDocumentChange(request); 212 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 213 } catch (NuxeoException e) { 214 facesMessages.add(ERROR, e.getMessage()); 215 } 216 } 217 218 public void submitUserRegistration(String configurationName) { 219 try { 220 docinfo.setDocumentId(navigationContext.getCurrentDocument().getId()); 221 docinfo.setDocumentTitle(navigationContext.getCurrentDocument().getTitle()); 222 doSubmitUserRegistration(configurationName); 223 resetPojos(); 224 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 225 } catch (NuxeoException e) { 226 facesMessages.add(ERROR, e.getMessage()); 227 } 228 } 229 230 public void submitMultipleUserRegistration(String configurationName) throws AddressException { 231 if (StringUtils.isBlank(multipleEmails)) { 232 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.registration.multiple.empty")); 233 return; 234 } 235 docinfo.setDocumentId(navigationContext.getCurrentDocument().getId()); 236 237 InternetAddress[] emails = splitAddresses(multipleEmails); 238 for (InternetAddress email : emails) { 239 userinfo.setLogin(email.getAddress()); 240 userinfo.setEmail(email.getAddress()); 241 242 log.debug("Request email: " + email + " with multiple invitation."); 243 doSubmitUserRegistration(configurationName); 244 } 245 resetPojos(); 246 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 247 } 248 249 protected InternetAddress[] splitAddresses(String emails) throws AddressException { 250 return StringUtils.isNotBlank(emails) ? InternetAddress.parse(emails.replace(MULTIPLE_EMAILS_SEPARATOR, ","), 251 false) : new InternetAddress[] {}; 252 } 253 254 public void validateMultipleUser(FacesContext context, UIComponent component, Object value) { 255 if (value instanceof String) { 256 try { 257 splitAddresses((String) value); 258 return; 259 } catch (AddressException e) { 260 // Nothing to do, error is handled after 261 } 262 } 263 264 FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, 265 "label.request.error.multiple.emails"), null); 266 267 // also add global message 268 context.addMessage(null, message); 269 throw new ValidatorException(message); 270 } 271 272 public boolean getCanValidate() { 273 boolean canDelete = !documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST); 274 for (DocumentModel doc : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 275 canDelete &= isDocumentValidable(doc); 276 } 277 return canDelete; 278 } 279 280 protected boolean isDocumentValidable(DocumentModel doc) { 281 return "accepted".equals(doc.getCurrentLifeCycleState()); 282 } 283 284 public boolean getCanDelete() { 285 boolean canDelete = !documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST); 286 for (DocumentModel doc : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 287 canDelete &= isDocumentDeletable(doc); 288 } 289 return canDelete; 290 } 291 292 protected boolean isDocumentDeletable(DocumentModel doc) { 293 return !"validated".equals(doc.getCurrentLifeCycleState()); 294 } 295 296 public boolean getCanRevive() { 297 boolean canRevive = !documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST); 298 for (DocumentModel doc : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 299 canRevive &= isDocumentRevivable(doc); 300 } 301 return canRevive; 302 } 303 304 protected boolean isDocumentRevivable(DocumentModel doc) { 305 return "accepted".equals(doc.getCurrentLifeCycleState()); 306 } 307 308 public void validateUserRegistration() { 309 if (!documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST)) { 310 try { 311 for (DocumentModel registration : documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)) { 312 userRegistrationService.validateRegistration(registration.getId(), 313 new HashMap<String, Serializable>()); 314 } 315 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 316 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.validate.request")); 317 documentsListsManager.resetWorkingList(REQUEST_DOCUMENT_LIST); 318 } catch (NuxeoException e) { 319 log.warn("Unable to validate registration: " + e.getMessage()); 320 log.info(e); 321 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.validate.request")); 322 } 323 } 324 } 325 326 public void reviveUserRegistration() { 327 if (!documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST)) { 328 try { 329 userRegistrationService.reviveRegistrationRequests(documentManager, 330 documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)); 331 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 332 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.revive.request")); 333 documentsListsManager.resetWorkingList(REQUEST_DOCUMENT_LIST); 334 } catch (NuxeoException e) { 335 log.warn("Unable to revive user: " + e.getMessage()); 336 log.info(e); 337 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.revive.request")); 338 } 339 } 340 } 341 342 public void deleteUserRegistration() { 343 if (!documentsListsManager.isWorkingListEmpty(REQUEST_DOCUMENT_LIST)) { 344 try { 345 userRegistrationService.deleteRegistrationRequests(documentManager, 346 documentsListsManager.getWorkingList(REQUEST_DOCUMENT_LIST)); 347 Events.instance().raiseEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 348 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.delete.request")); 349 documentsListsManager.resetWorkingList(REQUEST_DOCUMENT_LIST); 350 } catch (NuxeoException e) { 351 log.warn("Unable to delete user request:" + e.getMessage()); 352 log.info(e); 353 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.delete.request")); 354 } 355 356 } 357 } 358 359 protected void doSubmitUserRegistration(String configurationName) { 360 if (StringUtils.isBlank(configurationName)) { 361 configurationName = CONFIGURATION_NAME; 362 } 363 364 try { 365 userRegistrationService.submitRegistrationRequest(configurationName, userinfo, docinfo, 366 getAdditionalsParameters(), EMAIL, false, documentManager.getPrincipal().getName()); 367 368 facesMessages.add(INFO, resourcesAccessor.getMessages().get("label.user.invited.success")); 369 } catch (NuxeoException e) { 370 log.info("Unable to register user: " + e.getMessage()); 371 log.debug(e, e); 372 facesMessages.add(ERROR, resourcesAccessor.getMessages().get("label.unable.invite"), userinfo.getLogin()); 373 facesMessages.add(ERROR, e.getMessage()); 374 } 375 } 376 377 protected Map<String, Serializable> getAdditionalsParameters() { 378 Map<String, Serializable> additionalsInfo = new HashMap<String, Serializable>(); 379 try { 380 additionalsInfo.put("docinfo:documentTitle", navigationContext.getCurrentDocument().getTitle()); 381 if (copyOwner) { 382 additionalsInfo.put("registration:copyTo", ((NuxeoPrincipal) documentManager.getPrincipal()).getEmail()); 383 } 384 additionalsInfo.put("registration:comment", comment); 385 } catch (NuxeoException e) { 386 // log it silently as it will break anything 387 log.debug(e, e); 388 } 389 return additionalsInfo; 390 } 391 392 @Observer({ EventNames.DOCUMENT_CHANGED }) 393 public void resetPojos() { 394 userinfo = new UserRegistrationInfo(); 395 docinfo = new DocumentRegistrationInfo(); 396 multipleEmails = ""; 397 copyOwner = false; 398 comment = ""; 399 } 400 401 @Observer({ REQUESTS_DOCUMENT_LIST_CHANGED }) 402 public void refreshContentViewCache() { 403 contentViewActions.refreshOnSeamEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 404 contentViewActions.resetPageProviderOnSeamEvent(REQUESTS_DOCUMENT_LIST_CHANGED); 405 } 406 407 public void validateUsernameEmail(FacesContext context, UIComponent component, Object value) { 408 String email = (String) ((UIInput) component.findComponent(WIDGET_COMPONENT_EMAIL_ID)).getLocalValue(); 409 NuxeoPrincipal principal = userManager.getPrincipal((String) value); 410 if (principal == null || email == null) { 411 return; 412 } 413 if (!email.equals(principal.getEmail())) { 414 FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, 415 "label.unicity.usernamepwd"), null); 416 context.addMessage(null, message); 417 throw new ValidatorException(message); 418 } 419 } 420}