/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openehealth.coala.beans; import java.io.Serializable; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.UIOutput; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.event.AjaxBehaviorEvent; import javax.faces.event.ComponentSystemEvent; import javax.faces.event.ValueChangeEvent; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import javax.persistence.Transient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.richfaces.component.UIExtendedDataTable; import org.richfaces.component.UIPopupPanel; import org.richfaces.component.UIRegion; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.openehealth.coala.domain.CoalaAuthor; import org.openehealth.coala.domain.ConsentSortParameter; import org.openehealth.coala.domain.FindPatientConsentResult; import org.openehealth.coala.domain.Patient; import org.openehealth.coala.domain.PatientConsent; import org.openehealth.coala.domain.PatientConsentPolicy; import org.openehealth.coala.exception.CdaXmlTransformerException; import org.openehealth.coala.interfacing.CDATransformationService; import org.openehealth.coala.interfacing.ConsentCreationService; /** * Represents a patients privacy consent. It will be used if a new consent is * created, or an existing gets deleted. * * @author astiefer, mkuballa */ @Component @Scope("session") public class ConsentBean implements Serializable { private static final long serialVersionUID = 1L; private static final Log LOG = LogFactory.getLog(ConsentBean.class); private PatientConsentPolicy policy = PatientConsentPolicy.ONE; // default value private Date validFrom; private Date validUntil; private CoalaAuthor author; @Transient private DataModel<PatientConsent> consents = new ListDataModel<PatientConsent>(); private List<PatientConsent> consentList; private Collection<Object> selectionConsent; private PatientConsent selectedConsent; private Patient selectedPatient; private String patientString = ""; // SORTING private ConsentSortParameter sortParameter = ConsentSortParameter.CREATION_DATE_NEWEST_FIRST; // SELECTING POLICIES VIA SELECT-MENU. private static Map<String, PatientConsentPolicy> selectablePolicies; private String selectedPolicy = policy.getShortName(); private boolean successfulRegistration; private boolean errorfulRegistration; private boolean validationSuccessful; /* * Handle on the coala-communication layer */ @Transient @Autowired private ConsentCreationService consentCreationService; /* * Handle on the CDA/Consent service layer */ @Transient @Autowired private CDATransformationService cdaService; /* * Managed bean injection to get current locale which is needed for I18n * e.g. error messages/warnings. */ @Transient @Autowired private LocaleHandler localeHandler; /** * @return the ConsentCreationService */ public ConsentCreationService getConsentCreationService() { return consentCreationService; } /** * @param sets the ConsentCreationService */ public void setConsentCreationService(ConsentCreationService consentCreationService) { this.consentCreationService = consentCreationService; LOG.info("ConsentCreationService configured and ready... [OK]"); } /** * @return the policy */ public PatientConsentPolicy getPolicy() { return policy; } /** * @param policy the policy to set */ public void setPolicy(PatientConsentPolicy policy) { this.policy = policy; } /** * @return the validFrom */ public Date getValidFrom() { return validFrom; } /** * @param validFrom the validFrom to set */ public void setValidFrom(Date validFrom) { /* * HACK HERE! we get invalid Date object from RichFaces Calendar component which is exactly 1 Day in the past! so we fix it to have the proper date instance that is shown/selected by the user! */ if(validFrom!=null) { Calendar cal = new GregorianCalendar(); cal.setTime(validFrom); cal.add(Calendar.DAY_OF_MONTH, 1); this.validFrom = cal.getTime(); } else { this.validFrom = validFrom; } } /** * @return the validUntil */ public Date getValidUntil() { return validUntil; } /** * @param validUntil the validUntil to set */ public void setValidUntil(Date validUntil) { /* * HACK HERE! we get invalid Date object from RichFaces Calendar component which is exactly 1 Day in the past! so we fix it to have the proper date instance that is shown/selected by the user! */ if(validUntil!=null) { Calendar cal = new GregorianCalendar(); cal.setTime(validUntil); cal.add(Calendar.DAY_OF_MONTH, 1); this.validUntil = cal.getTime(); } else { this.validUntil = validUntil; } } /** * Selections-Listener for data table displaying {@link PatientConsent} * instances. * * @param event * Provided by UI context. */ public void selectionListenerConsent(AjaxBehaviorEvent event) { FacesContext fc = FacesContext.getCurrentInstance(); String messages = fc.getApplication().getMessageBundle(); Locale locale = new Locale(localeHandler.getLocale()); ResourceBundle bundle = ResourceBundle.getBundle(messages, locale); UIExtendedDataTable dataTable = (UIExtendedDataTable) event .getComponent(); Object originalKey = dataTable.getRowKey(); for (Object selectionKey : selectionConsent) { dataTable.setRowKey(selectionKey); if (dataTable.isRowAvailable()) { selectedConsent = (PatientConsent) dataTable.getRowData(); LOG.info("[CONSENT SELECTED] by " + selectedConsent.getAuthor()); LOG.info("[CONSENT SELECTED] Patient" + selectedPatient.getLastName()); UIRegion consentResultPanel = (UIRegion) dataTable.getParent(); UIOutput renderedConsentText = (UIOutput) consentResultPanel .findComponent("renderedConsentText"); String consentAsHTML = ""; try { consentAsHTML = cdaService.transformToHTML(selectedPatient, selectedConsent.getValidFrom(), selectedConsent.getValidUntil(), selectedConsent.getPolicyType(), selectedConsent.getAuthor()); } catch (IllegalArgumentException e) { consentAsHTML = bundle.getString("errors.htmlTransformationFailed"); LOG.warn(e.getMessage()); } catch (CdaXmlTransformerException e) { consentAsHTML = bundle.getString("errors.htmlTransformationFailed"); LOG.warn(e.getMessage()); } renderedConsentText.setValue(consentAsHTML); UIPopupPanel consentDisplayPanel = (UIPopupPanel) consentResultPanel .findComponent("consentDisplayPanel"); LOG.info("[CONSENT SELECTED] setShow"); consentDisplayPanel.setShow(true); } else { LOG.warn("ROW NOT AVAILABLE"); } } dataTable.setRowKey(originalKey); } /** * Searches the {@link PatientConsent} of the {@link Patient} currently * selected in the patient data table (see * {@link ConsentBean#selectionListener(AjaxBehaviorEvent)}) and sets them * in the consent data table of the UI. * * @return Always returns "patientSeach" */ public String search() { consentList = null; consentList = new ArrayList<PatientConsent>(); long start = System.currentTimeMillis(); // perform query against PXS finally FindPatientConsentResult findPatientConsentResult = consentCreationService.getPatientConsents(selectedPatient, sortParameter); long end = System.currentTimeMillis(); LOG.info("PXS iti-18-43 query took " + (end - start) + " ms."); if (findPatientConsentResult != null) { consentList = findPatientConsentResult.getPatientConsents(); setConsents(new ListDataModel<PatientConsent>(consentList)); } buildPatientFullName(); return "patientSearch"; } /** * Builds the fullname of the patient for displaying in the UI */ private void buildPatientFullName() { setPatientString(selectedPatient.getLastName() + ", " + selectedPatient.getGivenName() + " (" + selectedPatient.getPatientID() + ")"); } /** * @param consents the consents to set */ public void setConsents(DataModel<PatientConsent> consents) { this.consents = consents; } /** * @return the consents */ public DataModel<PatientConsent> getConsents() { return consents; } /** * @param selection sets the selectionConset */ public void setConsentSelection(Collection<Object> selection) { this.selectionConsent = selection; } /** * @return the selectionConsent */ public Collection<Object> getConsentSelection() { return selectionConsent; } /** * @return the sortParameter */ public ConsentSortParameter getSortParameter() { return sortParameter; } /** * @param sortParameter the sortParameter to set */ public void setSortParameter(ConsentSortParameter sortParameter) { this.sortParameter = sortParameter; } /** * Sorts the table of consents by start date */ public void sortByStartDate() { if (sortParameter.equals(ConsentSortParameter.START_DATE_NEWEST_FIRST)) { setSortParameter(ConsentSortParameter.START_DATE_OLDEST_FIRST); } else { setSortParameter(ConsentSortParameter.START_DATE_NEWEST_FIRST); } search(); } /** * Sorts the table of consents by end date */ public void sortByEndDate() { if (sortParameter.equals(ConsentSortParameter.END_DATE_NEWEST_FIRST)) { setSortParameter(ConsentSortParameter.END_DATE_OLDEST_FIRST); } else { setSortParameter(ConsentSortParameter.END_DATE_NEWEST_FIRST); } search(); } /** * Sorts the table of consents by policy */ public void sortByPolicy() { if (sortParameter.equals(ConsentSortParameter.POLICY_TYPE_ASCENDING)) { setSortParameter(ConsentSortParameter.POLICY_TYPE_DESCENDING); } else { setSortParameter(ConsentSortParameter.POLICY_TYPE_ASCENDING); } search(); } /** * Sorts the table of consents by creation date */ public void sortByCreationDate() { if (sortParameter .equals(ConsentSortParameter.CREATION_DATE_NEWEST_FIRST)) { setSortParameter(ConsentSortParameter.CREATION_DATE_OLDEST_FIRST); } else { setSortParameter(ConsentSortParameter.CREATION_DATE_NEWEST_FIRST); } search(); } /** * Sorts the table of consents by author */ public void sortByAuthor() { if (sortParameter.equals(ConsentSortParameter.AUTHOR_ASCENDING)) { setSortParameter(ConsentSortParameter.AUTHOR_DESCENDING); } else { setSortParameter(ConsentSortParameter.AUTHOR_ASCENDING); } search(); } /** * Sorts the table of consents by activ */ public void sortByActive() { if (sortParameter.equals(ConsentSortParameter.EFFECTIVE_TRUE_FIRST)) { setSortParameter(ConsentSortParameter.EFFECTIVE_FALSE_FIRST); } else { setSortParameter(ConsentSortParameter.EFFECTIVE_TRUE_FIRST); } search(); } /** * @param patientString the patientString to set */ public void setPatientString(String patientString) { this.patientString = patientString; } /** * @return the patientString */ public String getPatientString() { return patientString; } static { selectablePolicies = new LinkedHashMap<String, PatientConsentPolicy>(); selectablePolicies.put(PatientConsentPolicy.ONE.getShortName(), PatientConsentPolicy.ONE); selectablePolicies.put(PatientConsentPolicy.TWO.getShortName(), PatientConsentPolicy.TWO); selectablePolicies.put(PatientConsentPolicy.THREE.getShortName(), PatientConsentPolicy.THREE); selectablePolicies.put(PatientConsentPolicy.FOUR.getShortName(), PatientConsentPolicy.FOUR); selectablePolicies.put(PatientConsentPolicy.FIVE.getShortName(), PatientConsentPolicy.FIVE); } /** * Method gets called when the user selects a policy via the select-menu. It * is needed to view the current policy in the draft view. * * @param e */ public void policyChanged(ValueChangeEvent e) { // assign new policy policy = getPolicyByNr(e.getNewValue().toString()); selectedPolicy = policy.getShortName(); } /** * @return the selectablePolicies */ public Map<String, PatientConsentPolicy> getSelectablePolicyInMap() { return ConsentBean.selectablePolicies; } /** * @return the selectedPolicy */ public String getSelectablePolicy() { return selectedPolicy; } /** * @param policy the policy to set */ public void setSelectablePolicy(String policy) { selectedPolicy = getPolicyByNr(policy).getShortName(); } /** * Cleans the outdated view state */ public void cleanOutdatedViewState() { this.selectionConsent = new ArrayList<Object>(); this.selectedConsent = null; this.selectedPatient = null; } /** * Cleans the ConsentBean for a new PatientSearch */ public void cleanForNewPatientSearch() { this.consentList = new ArrayList<PatientConsent>(); this.consents = new ListDataModel<PatientConsent>(); this.patientString = ""; this.selectedConsent = null; this.selectionConsent = new ArrayList<Object>(); } /** * Cleans the current selection * @param event */ public void cleanCurrentSelection(AjaxBehaviorEvent event) { this.selectionConsent = new ArrayList<Object>(); this.selectedConsent = null; LOG.info("[BAEBAEMMMM!]: " + event.getComponent().getClientId()); } /** * @param selectedPatient the selectedPatient to set */ public void setSelectedPatient(Patient selectedPatient) { this.selectedPatient = selectedPatient; } /** * Method to validate input parameters (validFrom and validUntil) of consent * creation. This is needed to have proper error notification on missing * date input at consent creation view. * * @param event */ public void validateCreateConsentParameters(ComponentSystemEvent event) { FacesContext fc = FacesContext.getCurrentInstance(); String messages = fc.getApplication().getMessageBundle(); Locale locale = new Locale(localeHandler.getLocale()); ResourceBundle bundle = ResourceBundle.getBundle(messages, locale); UIComponent components = event.getComponent(); UIInput validFromDateInput = (UIInput) components .findComponent("validFromInput"); UIInput validUntilDateInput = (UIInput) components .findComponent("validUntilInput"); boolean validFromDateEmpty = false; boolean validUntilDateEmpty = false; if (validFromDateInput.getLocalValue() == null || validFromDateInput.getLocalValue().toString().trim().isEmpty()) { validFromDateEmpty = true; } if (validUntilDateInput.getLocalValue() == null || validUntilDateInput.getLocalValue().toString().trim().isEmpty()) { validUntilDateEmpty = true; } if (validFromDateEmpty || validUntilDateEmpty) { validationSuccessful = false; FacesMessage msg = new FacesMessage( bundle.getString("errors.nonEmptyValidationDates"), ""); msg.setSeverity(FacesMessage.SEVERITY_WARN); fc.addMessage(components.getClientId(), msg); // passed to the Render Response phase fc.renderResponse(); } //validates if validFrom is after or equals validUntil Date from = (Date) validFromDateInput.getValue(); Date until = (Date) validUntilDateInput.getValue(); if(from==null || until == null) { validationSuccessful = false; FacesMessage msg = new FacesMessage("Please provide valid date input values for From AND Until.", ""); msg.setSeverity(FacesMessage.SEVERITY_WARN); fc.addMessage(components.getClientId(), msg); // passed to the Render Response phase fc.renderResponse(); } else if(from != null && until != null) { if (from.after(until) || from.equals(until)){ validationSuccessful = false; FacesMessage msg = new FacesMessage( bundle.getString("errors.fromBeforeUntil"), ""); msg.setSeverity(FacesMessage.SEVERITY_WARN); fc.addMessage(components.getClientId(), msg); // passed to the Render Response phase fc.renderResponse(); } // all checks passed now, set validationSuccessful to true, which causes the "Register" button to become active! else { LOG.info("Consent creation validation has passed now."); validationSuccessful = true; } } else { validationSuccessful = false; FacesMessage msg = new FacesMessage("Please provide valid date input values for From AND Until.", ""); msg.setSeverity(FacesMessage.SEVERITY_WARN); fc.addMessage(components.getClientId(), msg); // passed to the Render Response phase fc.renderResponse(); } } /** * Overriding this method ensures that rich:calender validation is not done * by standard validation procedure but by our own * {@link ConsentBean#validateCreateConsentParameters(ComponentSystemEvent)} * method. */ public void validator(javax.faces.context.FacesContext fc, javax.faces.component.UIComponent component, java.lang.Object object) { // JUST DO NOT VALIDATE ANYTHING HERE -> all done in // validateCreateConsentParameters... } /** * Gets the logged-in user and sets him as an author. * Then takes the 5 parameters(patient, validFrom, validUntil, policy, author) which are * given by the user and puts them into a patient consent and tries to send it to pxsQuery. * * @return */ public String registerConsent() { FacesContext fc = FacesContext.getCurrentInstance(); // Replaced dependency to eHF with fixed mocks for the time being. author = new CoalaAuthor("testTitle", "testFirstName", "testLastName"); UIViewRoot uiRoot = FacesContext.getCurrentInstance().getViewRoot(); try { // try to register now, as we have all relevant data for it consentCreationService.createPatientConsent( selectedPatient, validFrom,validUntil, policy, author); setSuccessfulRegistration(true); setErrorfulRegistration(false); // show successful creation panel to the user UIPopupPanel consentCreationSuccessDialog = (UIPopupPanel) uiRoot.findComponent("consentCreationSuccessDialog"); if(consentCreationSuccessDialog!=null) { consentCreationSuccessDialog.setShow(true); LOG.info("[CONSENT CREATION] ok, showing success PopUpPanel"); } // cleanup selected values here, as they are outdated now. setValidFrom(null); setValidUntil(null); setPolicy(PatientConsentPolicy.ONE); setValidationSuccessful(false); return "patientSearch"; } catch (Throwable t) { setErrorfulRegistration(true); setSuccessfulRegistration(false); UIPopupPanel consentCreationErrorDialog = (UIPopupPanel) uiRoot.findComponent("consentCreationErrorDialog"); if(consentCreationErrorDialog != null) { consentCreationErrorDialog.setShow(true); LOG.warn("[CONSENT CREATION] failed, showing error PopUpPanel"); } setValidationSuccessful(false); FacesMessage msg = new FacesMessage("Could not create consent."); msg.setSeverity(FacesMessage.SEVERITY_ERROR); fc.addMessage(null,msg); LOG.error(t.getLocalizedMessage(), t); // TODO Maybe add Facesmessage for display in ErrorPanelDialog } return "createConsent"; } /** * Returns the selected policy by the number. * @param nr number of the selected policy (ONE, TWO, THREE, FOUR or FIVE) * @return selected policy */ public PatientConsentPolicy getPolicyByNr(String nr){ if (nr.equals("ONE")) { return PatientConsentPolicy.ONE; } else if (nr.equals("TWO")) { return PatientConsentPolicy.TWO; } else if (nr.equals("THREE")) { return PatientConsentPolicy.THREE; } else if (nr.equals("FOUR")) { return PatientConsentPolicy.FOUR; } else { return PatientConsentPolicy.FIVE; } } /** * @return the selectedConsent */ public PatientConsent getSelectedConsent() { return selectedConsent; } /** * @param selectedConsent the selectedConsent to set */ public void setSelectedConsent(PatientConsent selectedConsent) { this.selectedConsent = selectedConsent; } public boolean isSuccessfulRegistration() { return successfulRegistration; } public void setSuccessfulRegistration(boolean successfulRegistration) { this.successfulRegistration = successfulRegistration; } public boolean isErrorfulRegistration() { return errorfulRegistration; } public void setErrorfulRegistration(boolean errorfulRegistration) { this.errorfulRegistration = errorfulRegistration; } public void setValidationSuccessful(boolean validationSuccessful) { this.validationSuccessful = validationSuccessful; } public boolean isValidationSuccessful() { return validationSuccessful; } }