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}