org.libreplan.web.common.ConfigurationController.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.web.common.ConfigurationController.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2012 Igalia, S.L.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.libreplan.web.common;

import static org.libreplan.web.I18nHelper._;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.List;
import java.util.ConcurrentModificationException;
import java.util.Properties;
import java.util.ArrayList;
import java.util.Set;
import java.util.Map;
import java.util.Collections;
import java.util.HashSet;
import java.util.Arrays;

import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.AuthenticationFailedException;
import javax.mail.MessagingException;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.client.WebClient;
import org.libreplan.business.calendars.entities.BaseCalendar;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.common.entities.Configuration;
import org.libreplan.business.common.entities.Connector;
import org.libreplan.business.common.entities.ConnectorProperty;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.entities.EntitySequence;
import org.libreplan.business.common.entities.LDAPConfiguration;
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
import org.libreplan.business.common.entities.PredefinedConnectorProperties;
import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.common.entities.ProgressType;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.importers.JiraRESTClient;
import org.libreplan.importers.TimSoapClient;
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
import org.libreplan.web.security.SecurityUtils;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.web.context.ContextLoaderListener;
import org.zkoss.util.media.Media;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zk.ui.util.GenericForwardComposer;

import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Grid;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Radiogroup;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Row;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.RowRenderer;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Radio;
import org.zkoss.zul.Button;
import org.zkoss.zul.Label;
import org.zkoss.zul.Rows;
import org.zkoss.zul.SimpleListModel;
import org.zkoss.zul.Messagebox;

import org.zkoss.zul.Window;
import org.zkoss.zul.impl.InputElement;

/**
 * Controller for {@link Configuration} entity.
 *
 * @author Manuel Rego Casasnovas <mrego@igalia.com>
 * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
 * @author Cristina Alavarino Perez <cristina.alvarino@comtecsf.es>
 * @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
 * @author Vova Perebykivskyi <vova@libreplan-enterprise.com>
 */
public class ConfigurationController extends GenericForwardComposer {

    private static final Log LOG = LogFactory.getLog(ConfigurationController.class);

    private final ProgressTypeRenderer progressTypeRenderer = new ProgressTypeRenderer();

    private Window configurationWindow;

    private BandboxSearch defaultCalendarBandboxSearch;

    private Listbox lbTypeProgress;

    private IConfigurationModel configurationModel;

    private IConfigurationDAO configurationDAO;

    private IMessagesForUser messages;

    private Component messagesContainer;

    private Grid entitySequencesGrid;

    private Combobox entityCombo;

    private Intbox numDigitBox;

    private Textbox prefixBox;

    private Textbox ldapGroupPath;

    private Radiogroup strategy;

    private Combobox connectorCombo;

    private Grid connectorPropertriesGrid;

    private Connector selectedConnector;

    private Combobox protocolsCombobox;

    private Textbox emailUsernameTextbox;

    private Textbox emailPasswordTextbox;

    private Textbox emailSenderTextbox;

    private Textbox companyLogoURL;

    private String STARTTLS_PROTOCOL = "STARTTLS";

    private String LOGO_PREVIEW_COMPONENT = "logoPreview";

    public ConfigurationController() {
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);

        injectsObjects();

        comp.setAttribute("configurationController", this, true);
        configurationModel.init();

        defaultCalendarBandboxSearch.setListboxEventListener(Events.ON_SELECT, event -> {
            Listitem selectedItem = (Listitem) ((SelectEvent) event).getSelectedItems().iterator().next();
            setDefaultCalendar(selectedItem.getValue());
        });

        initializeProgressTypeList();
        messages = new MessagesForUser(messagesContainer);
        reloadEntitySequences();
        loadRoleStrategyRows();
    }

    private void injectsObjects() {
        if (configurationModel == null) {
            configurationModel = (IConfigurationModel) SpringUtil.getBean("configurationModel");
        }

        if (configurationDAO == null) {
            configurationDAO = (IConfigurationDAO) SpringUtil.getBean("configurationDAO");
        }
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void changeRoleStrategy() {
        this.getLdapConfiguration().setLdapGroupStrategy("group".equals(strategy.getSelectedItem().getValue()));
        loadRoleStrategyRows();
    }

    private void loadRoleStrategyRows() {
        if (getLdapConfiguration().getLdapGroupStrategy()) {
            strategy.setSelectedIndex(0);
            ldapGroupPath.setDisabled(false);
        } else {
            strategy.setSelectedIndex(1);
            ldapGroupPath.setDisabled(true);
        }
    }

    private void initializeProgressTypeList() {
        lbTypeProgress.addEventListener(Events.ON_SELECT, new EventListener() {

            @Override
            public void onEvent(Event event) {
                Listitem selectedItem = getSelectedItem((SelectEvent) event);

                if (selectedItem != null) {
                    ProgressType progressType = selectedItem.getValue();
                    configurationModel.setProgressType(progressType);
                }
            }

            private Listitem getSelectedItem(SelectEvent event) {
                final Set<Listitem> selectedItems = event.getSelectedItems();
                return selectedItems.iterator().next();
            }

        });
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public List<ProgressType> getProgressTypes() {
        return configurationModel.getProgressTypes();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public ProgressType getSelectedProgressType() {
        return configurationModel.getProgressType();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setSelectedProgressType(ProgressType progressType) {
        configurationModel.setProgressType(progressType);
    }

    public List<BaseCalendar> getCalendars() {
        return configurationModel.getCalendars();
    }

    public BaseCalendar getDefaultCalendar() {
        return configurationModel.getDefaultCalendar();
    }

    public void setDefaultCalendar(BaseCalendar calendar) {
        configurationModel.setDefaultCalendar(calendar);
    }

    public void save() throws InterruptedException {

        if (getSelectedConnector() != null && "E-mail".equals(getSelectedConnector().getName())
                && !areEmailFieldsValid()) {

            messages.clearMessages();
            messages.showMessage(Level.ERROR, _("Check all fields"));

        } else {
            ConstraintChecker.isValid(configurationWindow);
            if (checkValidEntitySequenceRows()) {
                try {
                    configurationModel.confirm();
                    configurationModel.init();
                    messages.showMessage(Level.INFO, _("Changes saved"));

                    // Send data to server
                    if (!SecurityUtils.isGatheredStatsAlreadySent && configurationDAO
                            .getConfigurationWithReadOnlyTransaction().isAllowedToGatherUsageStatsEnabled()) {

                        sendDataToServer();
                    }

                    if (getSelectedConnector() != null
                            && !configurationModel.scheduleOrUnscheduleJobs(getSelectedConnector())) {

                        messages.showMessage(Level.ERROR,
                                _("Scheduling or unscheduling of jobs for this connector is not completed"));
                    }

                    reloadWindow();
                    reloadEntitySequences();
                    reloadConnectors();

                } catch (ValidationException e) {
                    messages.showInvalidValues(e);
                } catch (ConcurrentModificationException e) {
                    messages.showMessage(Level.ERROR, e.getMessage());
                    configurationModel.init();
                    reloadWindow();
                    reloadEntitySequences();
                    reloadConnectors();
                }
            }
        }
    }

    private void sendDataToServer() {
        GatheredUsageStats gatheredUsageStats = new GatheredUsageStats();
        gatheredUsageStats.sendGatheredUsageStatsToServer();
        SecurityUtils.isGatheredStatsAlreadySent = true;
    }

    public void cancel() throws InterruptedException {
        configurationModel.cancel();
        messages.clearMessages();
        messages.showMessage(Level.INFO, _("Changes have been canceled"));
        reloadWindow();
        reloadEntitySequences();
        reloadConnectors();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void testLDAPConnection() {
        LdapContextSource source = new LdapContextSource();

        source.setUrl(configurationModel.getLdapConfiguration().getLdapHost() + ":"
                + configurationModel.getLdapConfiguration().getLdapPort());

        source.setBase(configurationModel.getLdapConfiguration().getLdapBase());
        source.setUserDn(configurationModel.getLdapConfiguration().getLdapUserDn());
        source.setPassword(configurationModel.getLdapConfiguration().getLdapPassword());
        source.setDirObjectFactory(DefaultDirObjectFactory.class);
        source.setPooled(false);
        try {
            source.afterPropertiesSet();
        } catch (Exception e) {
            e.printStackTrace();
        }

        LdapTemplate template = new LdapTemplate(source);
        try {
            /* TODO resolve deprecated */
            template.authenticate(DistinguishedName.EMPTY_PATH,
                    new EqualsFilter(configurationModel.getLdapConfiguration().getLdapUserId(), "test").toString(),
                    "test");

            messages.showMessage(Level.INFO, _("LDAP connection was successful"));
        } catch (Exception e) {
            LOG.info(e);
            messages.showMessage(Level.ERROR, _("Cannot connect to LDAP server"));
        }
    }

    /**
     * Tests connection.
     *
     * Used in configuration.zul
     * Should be public!
     */
    public void testConnection() {
        if (selectedConnector == null) {
            messages.showMessage(Level.ERROR, _("Please select a connector to test it"));
            return;
        }

        Map<String, String> properties = selectedConnector.getPropertiesAsMap();
        String url = properties.get(PredefinedConnectorProperties.SERVER_URL);
        String username = properties.get(PredefinedConnectorProperties.USERNAME);
        String password = properties.get(PredefinedConnectorProperties.PASSWORD);

        if (selectedConnector.getName().equals(PredefinedConnectors.TIM.getName())) {
            testTimConnection(url, username, password);

        } else if (selectedConnector.getName().equals(PredefinedConnectors.JIRA.getName())) {
            testJiraConnection(url, username, password);

        } else if (selectedConnector.getName().equals(PredefinedConnectors.EMAIL.getName())) {
            String host = properties.get(PredefinedConnectorProperties.HOST);
            username = properties.get(PredefinedConnectorProperties.EMAIL_USERNAME);
            password = properties.get(PredefinedConnectorProperties.EMAIL_PASSWORD);
            String port = properties.get(PredefinedConnectorProperties.PORT);
            testEmailConnection(host, port, username, password);
        } else {
            throw new RuntimeException("Unknown connector");
        }
    }

    /**
     * Test tim connection.
     *
     * @param url
     *            the url of the server
     * @param username
     *            the username
     * @param password
     *            the password
     */
    private void testTimConnection(String url, String username, String password) {
        if (TimSoapClient.checkAuthorization(url, username, password)) {
            messages.showMessage(Level.INFO, _("Tim connection was successful"));
        } else {
            messages.showMessage(Level.ERROR, _("Cannot connet to Tim server"));
        }
    }

    /**
     * Test JIRA connection.
     *
     * @param url
     *            the url
     * @param username
     *            the username
     * @param password
     *            the password
     */
    private void testJiraConnection(String url, String username, String password) {

        try {

            WebClient client = WebClient.create(url);
            client.path(JiraRESTClient.PATH_AUTH_SESSION).accept(MediaType.APPLICATION_JSON,
                    MediaType.APPLICATION_XML);

            org.libreplan.ws.common.impl.Util.addAuthorizationHeader(client, username, password);

            Response response = client.get();

            if (response.getStatus() == Status.OK.getStatusCode()) {
                messages.showMessage(Level.INFO, _("JIRA connection was successful"));
            } else {
                LOG.error("Status code: " + response.getStatus());
                messages.showMessage(Level.ERROR, _("Cannot connect to JIRA server"));
            }

        } catch (Exception e) {
            LOG.error(e);
            messages.showMessage(Level.ERROR, _("Cannot connect to JIRA server"));
        }
    }

    /**
     * Test E-mail connection.
     *
     * @param host
     *            the host
     * @param port
     *            the port
     * @param username
     *            the username
     * @param password
     *            the password
     */
    private void testEmailConnection(String host, String port, String username, String password) {
        Properties props = System.getProperties();
        Transport transport = null;

        try {
            if ("SMTP".equals(protocolsCombobox.getSelectedItem().getLabel())) {
                props.setProperty("mail.smtp.port", port);
                props.setProperty("mail.smtp.host", host);
                props.setProperty("mail.smtp.connectiontimeout", Integer.toString(3000));
                Session session = Session.getInstance(props, null);

                transport = session.getTransport("smtp");
                if ("".equals(username) && "".equals(password)) {
                    transport.connect();
                }
            } else if (STARTTLS_PROTOCOL.equals(protocolsCombobox.getSelectedItem().getLabel())) {
                props.setProperty("mail.smtps.port", port);
                props.setProperty("mail.smtps.host", host);
                props.setProperty("mail.smtps.connectiontimeout", Integer.toString(3000));
                Session session = Session.getInstance(props, null);

                transport = session.getTransport("smtps");
                if (!"".equals(username) && password != null) {
                    transport.connect(host, username, password);
                }
            }

            messages.clearMessages();
            if (transport != null) {
                if (transport.isConnected()) {
                    messages.showMessage(Level.INFO, _("Connection successful!"));
                } else if (!transport.isConnected()) {
                    messages.showMessage(Level.WARNING, _("Connection unsuccessful"));
                }
            }
        } catch (AuthenticationFailedException e) {
            messages.clearMessages();
            messages.showMessage(Level.ERROR, _("Invalid credentials"));
        } catch (MessagingException e) {
            LOG.error(e);
            messages.clearMessages();
            messages.showMessage(Level.ERROR, _("Cannot connect"));
        } catch (Exception e) {
            LOG.error(e);
            messages.clearMessages();
            messages.showMessage(Level.ERROR, _("Failed to connect"));
        }
    }

    private boolean checkValidEntitySequenceRows() {
        Rows rows = entitySequencesGrid.getRows();
        List<Row> listRows = rows.getChildren();

        for (Row row : listRows) {

            EntitySequence seq = row.getValue();
            if (seq != null) {
                Textbox prefixBox = (Textbox) row.getChildren().get(2);
                if (!seq.isAlreadyInUse()) {
                    String errorMessage = this.validPrefix(seq, prefixBox.getValue());

                    if (errorMessage != null) {
                        throw new WrongValueException(prefixBox, errorMessage);
                    }
                }

                Intbox digitsBox = (Intbox) row.getChildren().get(3);
                try {
                    if (!seq.isAlreadyInUse()) {
                        seq.setNumberOfDigits(digitsBox.getValue());
                    }
                } catch (IllegalArgumentException e) {
                    throw new WrongValueException(digitsBox, _("number of digits must be between {0} and {1}",
                            EntitySequence.MIN_NUMBER_OF_DIGITS, EntitySequence.MAX_NUMBER_OF_DIGITS));
                }
            }

        }

        return true;
    }

    private void reloadWindow() {
        Util.reloadBindings(configurationWindow);
    }

    private void reloadEntitySequences() {
        entitySequencesGrid.setModel(new SimpleListModel<>(getAllEntitySequences().toArray()));
        entitySequencesGrid.invalidate();
    }

    private void reloadConnectors() {
        selectedConnector = configurationModel
                .getConnectorByName(selectedConnector != null ? selectedConnector.getName() : null);

        Util.reloadBindings(connectorCombo);
        Util.reloadBindings(connectorPropertriesGrid);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public String getCompanyCode() {
        return configurationModel.getCompanyCode();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setCompanyCode(String companyCode) {
        configurationModel.setCompanyCode(companyCode);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public String getCompanyLogoURL() {
        return configurationModel.getCompanyLogoURL();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setCompanyLogoURL(String companyLogoURL) {
        configurationModel.setCompanyLogoURL(companyLogoURL);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForCriterion() {
        return configurationModel.getGenerateCodeForCriterion();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForCriterion(Boolean generateCodeForCriterion) {
        configurationModel.setGenerateCodeForCriterion(generateCodeForCriterion);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForWorkReportType() {
        return configurationModel.getGenerateCodeForWorkReportType();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForWorkReportType(Boolean generateCodeForWorkReportType) {
        configurationModel.setGenerateCodeForWorkReportType(generateCodeForWorkReportType);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForCalendarExceptionType() {
        return configurationModel.getGenerateCodeForCalendarExceptionType();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForCalendarExceptionType(Boolean generateCodeForCalendarExceptionType) {
        configurationModel.setGenerateCodeForCalendarExceptionType(generateCodeForCalendarExceptionType);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForCostCategory() {
        return configurationModel.getGenerateCodeForCostCategory();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForCostCategory(Boolean generateCodeForCostCategory) {
        configurationModel.setGenerateCodeForCostCategory(generateCodeForCostCategory);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForLabel() {
        return configurationModel.getGenerateCodeForLabel();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForLabel(Boolean generateCodeForLabel) {
        configurationModel.setGenerateCodeForLabel(generateCodeForLabel);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForWorkReport() {
        return configurationModel.getGenerateCodeForWorkReport();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForWorkReport(Boolean generateCodeForWorkReport) {
        configurationModel.setGenerateCodeForWorkReport(generateCodeForWorkReport);
    }

    public Boolean getGenerateCodeForResources() {
        return configurationModel.getGenerateCodeForResources();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForResources(Boolean generateCodeForResources) {
        configurationModel.setGenerateCodeForResources(generateCodeForResources);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForTypesOfWorkHours() {
        return configurationModel.getGenerateCodeForTypesOfWorkHours();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForTypesOfWorkHours(Boolean generateCodeForTypesOfWorkHours) {
        configurationModel.setGenerateCodeForTypesOfWorkHours(generateCodeForTypesOfWorkHours);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForMaterialCategories() {
        return configurationModel.getGenerateCodeForMaterialCategories();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForMaterialCategories(Boolean generateCodeForMaterialCategories) {
        configurationModel.setGenerateCodeForMaterialCategories(generateCodeForMaterialCategories);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForExpenseSheets() {
        return configurationModel.getGenerateCodeForExpenseSheets();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForExpenseSheets(Boolean generateCodeForExpenseSheets) {
        configurationModel.setGenerateCodeForExpenseSheets(generateCodeForExpenseSheets);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void reloadGeneralConfiguration() {
        reloadWindow();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForUnitTypes() {
        return configurationModel.getGenerateCodeForUnitTypes();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForUnitTypes(Boolean generateCodeForUnitTypes) {
        configurationModel.setGenerateCodeForUnitTypes(generateCodeForUnitTypes);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getGenerateCodeForBaseCalendars() {
        return configurationModel.getGenerateCodeForBaseCalendars();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setGenerateCodeForBaseCalendars(Boolean generateCodeForBaseCalendars) {
        configurationModel.setGenerateCodeForBaseCalendars(generateCodeForBaseCalendars);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean isAutocompleteLogin() {
        return configurationModel.isAutocompleteLogin();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setAutocompleteLogin(Boolean autocompleteLogin) {
        configurationModel.setAutocompleteLogin(autocompleteLogin);
    }

    public void removeEntitySequence(EntitySequence entitySequence) {
        try {
            configurationModel.removeEntitySequence(entitySequence);
        } catch (IllegalArgumentException e) {
            messages.showMessage(Level.ERROR, e.getMessage());
        }
        reloadEntitySequences();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setMonteCarloMethodTabVisible(Boolean expandResourceLoadViewCharts) {
        configurationModel.setMonteCarloMethodTabVisible(expandResourceLoadViewCharts);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean isMonteCarloMethodTabVisible() {
        return configurationModel.isMonteCarloMethodTabVisible();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public ProgressTypeRenderer getProgressTypeRenderer() {
        return progressTypeRenderer;
    }

    private static class ProgressTypeRenderer implements ListitemRenderer {
        @Override
        public void render(Listitem listitem, Object o, int i) throws Exception {
            ProgressType progressType = (ProgressType) o;
            listitem.setLabel(_(progressType.getValue()));
            listitem.setValue(progressType);
        }
    }

    private class EntitySequenceGroupRenderer implements RowRenderer {
        @Override
        public void render(Row row, Object o, int i) throws Exception {
            EntitySequence entitySequence = (EntitySequence) o;
            final EntityNameEnum entityName = entitySequence.getEntityName();

            row.setValue(entityName);
            row.appendChild(new Label(_("{0} sequences", entityName.getDescription())));

            row.setValue(entitySequence);
            appendActiveRadiobox(row, entitySequence);
            appendPrefixTextbox(row, entitySequence);
            appendNumberOfDigitsInbox(row, entitySequence);
            appendLastValueInbox(row, entitySequence);
            appendOperations(row, entitySequence);

            if (entitySequence.isAlreadyInUse()) {
                row.setTooltiptext(_("Code sequence is already in use and cannot be updated"));
            }

            if ((row.getPreviousSibling() != null)
                    && !((EntitySequence) ((Row) row.getPreviousSibling()).getValue()).getEntityName()
                            .equals(entityName)) {

                row.setClass("separator");
            }
        }

        private void appendActiveRadiobox(final Row row, final EntitySequence entitySequence) {
            final Radio radiobox = Util.bind(new Radio(), entitySequence::isActive, value -> {
                updateOtherSequences(entitySequence);
                entitySequence.setActive(value);
                Util.reloadBindings(entitySequencesGrid);
                reloadEntitySequences();
            });

            row.appendChild(radiobox);
        }

        private void appendPrefixTextbox(Row row, final EntitySequence entitySequence) {
            final Textbox tempTextbox = new Textbox();
            tempTextbox.setWidth("200px");

            Textbox textbox = Util.bind(tempTextbox, entitySequence::getPrefix, value -> {
                try {
                    entitySequence.setPrefix(value);
                } catch (IllegalArgumentException e) {
                    throw new WrongValueException(tempTextbox, e.getMessage());
                }
            });

            textbox.setConstraint(checkConstraintFormatPrefix());

            if (entitySequence.isAlreadyInUse()) {
                textbox.setDisabled(true);
            }

            row.appendChild(textbox);
        }

        private void appendNumberOfDigitsInbox(Row row, final EntitySequence entitySequence) {
            final Intbox tempIntbox = new Intbox();

            Intbox intbox = Util.bind(tempIntbox, entitySequence::getNumberOfDigits, value -> {
                try {
                    entitySequence.setNumberOfDigits(value);
                } catch (IllegalArgumentException e) {
                    throw new WrongValueException(tempIntbox, _("number of digits must be between {0} and {1}",
                            EntitySequence.MIN_NUMBER_OF_DIGITS, EntitySequence.MAX_NUMBER_OF_DIGITS));
                }
            });

            intbox.setConstraint(checkConstraintNumberOfDigits());

            if (entitySequence.isAlreadyInUse()) {
                intbox.setDisabled(true);
            }

            row.appendChild(intbox);
        }

        private void appendLastValueInbox(Row row, final EntitySequence entitySequence) {
            Textbox textbox = Util.bind(new Textbox(), () -> EntitySequence
                    .formatValue(entitySequence.getNumberOfDigits(), entitySequence.getLastValue()));

            row.appendChild(textbox);
        }

        private void appendOperations(final Row row, final EntitySequence entitySequence) {
            final Button removeButton = Util.createRemoveButton(event -> {
                if (isLastOne(entitySequence)) {
                    showMessageNotDelete();
                } else {
                    removeEntitySequence(entitySequence);
                }
            });

            if (entitySequence.isAlreadyInUse()) {
                removeButton.setDisabled(true);
            }

            row.appendChild(removeButton);
        }
    }

    private void updateOtherSequences(final EntitySequence activeSequence) {
        for (EntitySequence sequence : getEntitySequences(activeSequence.getEntityName())) {
            sequence.setActive(false);
        }
    }

    private Constraint checkConstraintFormatPrefix() {
        return (comp, value) -> {

            Row row = (Row) comp.getParent();
            EntitySequence sequence = row.getValue();
            if (!sequence.isAlreadyInUse()) {
                String errorMessage = validPrefix(sequence, (String) value);
                if (errorMessage != null) {
                    throw new WrongValueException(comp, errorMessage);
                }
            }
        };
    }

    private String validPrefix(EntitySequence sequence, String prefixValue) {
        sequence.setPrefix(prefixValue);
        if (!configurationModel.checkPrefixFormat(sequence)) {

            String message = _(
                    "Invalid format prefix. Format prefix cannot be empty, contain '_' or contain whitespaces.");

            if (sequence.getEntityName().canContainLowBar()) {
                message = _("format prefix invalid. It cannot be empty or contain whitespaces.");
            }

            return message;
        }

        return null;
    }

    private Constraint checkConstraintNumberOfDigits() {
        return (comp, value) -> {
            Row row = (Row) comp.getParent();
            EntitySequence sequence = row.getValue();
            if (!sequence.isAlreadyInUse()) {
                Integer numberOfDigits = (Integer) value;
                try {
                    sequence.setNumberOfDigits(numberOfDigits);
                } catch (IllegalArgumentException e) {
                    throw new WrongValueException(comp, _("number of digits must be between {0} and {1}",
                            EntitySequence.MIN_NUMBER_OF_DIGITS, EntitySequence.MAX_NUMBER_OF_DIGITS));
                }
            }
        };
    }

    private void addEntitySequence(EntityNameEnum entityName, String prefix, Integer digits) {
        configurationModel.addEntitySequence(entityName, prefix, digits);
        reloadEntitySequences();
    }

    private List<EntitySequence> getEntitySequences(EntityNameEnum entityName) {
        return configurationModel.getEntitySequences(entityName);
    }

    private boolean isLastOne(EntitySequence sequence) {
        return getEntitySequences(sequence.getEntityName()).size() == 1;
    }

    private void showMessageNotDelete() {
        Messagebox.show(_("It can not be deleted. At least one sequence is necessary."), _("Deleting sequence"),
                Messagebox.OK, Messagebox.INFORMATION);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public EntitySequenceGroupRenderer getEntitySequenceGroupRenderer() {
        return new EntitySequenceGroupRenderer();
    }

    private List<EntitySequence> getAllEntitySequences() {
        List<EntitySequence> allSequences = new ArrayList<>();

        for (final EntityNameEnum entityName : EntityNameEnum.values()) {
            allSequences.addAll(this.getEntitySequences(entityName));
        }

        return allSequences;
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void addNewEntitySequence() {
        if (entityCombo != null && numDigitBox != null) {
            if (entityCombo.getSelectedItem() == null) {
                throw new WrongValueException(entityCombo, _("Select entity, please"));
            }

            if (prefixBox.getValue() == null || prefixBox.getValue().isEmpty()) {
                throw new WrongValueException(prefixBox, _("cannot be empty"));
            }

            try {
                addEntitySequence(entityCombo.getSelectedItem().getValue(), prefixBox.getValue(),
                        numDigitBox.getValue());

            } catch (IllegalArgumentException e) {
                throw new WrongValueException(numDigitBox, e.getMessage());
            }
        }
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public EntityNameEnum[] getEntityNames() {
        return EntityNameEnum.values();
    }

    /**
     * Tab LDAP properties.
     */
    public LDAPConfiguration getLdapConfiguration() {
        return configurationModel.getLdapConfiguration();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setLdapConfiguration(LDAPConfiguration ldapConfiguration) {
        configurationModel.setLdapConfiguration(ldapConfiguration);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public RowRenderer getAllUserRolesRenderer() {
        return (row, o, i) -> {
            final UserRole role = (UserRole) o;
            row.appendChild(new Label(role.getDisplayName()));

            final Textbox tempTextbox = new Textbox();
            Textbox textbox = Util.bind(tempTextbox, () -> {
                List<String> listRoles = configurationModel.getLdapConfiguration().getMapMatchingRoles()
                        .get(role.name());

                Collections.sort(listRoles);

                return StringUtils.join(listRoles, ";");
            }, value -> {
                // Created a set in order to avoid duplicates
                Set<String> rolesLdap = new HashSet<>(Arrays.asList(StringUtils.split(value, ";")));
                configurationModel.getLdapConfiguration().setConfigurationRolesLdap(role.name(), rolesLdap);
            });

            textbox.setWidth("300px");
            row.appendChild(textbox);
        };
    }

    public UserRole[] getRoles() {
        return UserRole.values();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public boolean isChangedDefaultPasswdAdmin() {
        return configurationModel.isChangedDefaultPasswdAdmin();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public boolean isLdapPropertyStrategy() {
        return !getLdapConfiguration().getLdapGroupStrategy();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public boolean isCheckNewVersionEnabled() {
        return configurationModel.isCheckNewVersionEnabled();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setCheckNewVersionEnabled(boolean checkNewVersionEnabled) {
        configurationModel.setCheckNewVersionEnabled(checkNewVersionEnabled);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Set<String> getCurrencies() {
        return configurationModel.getCurrencies();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public ListitemRenderer getCurrencyRenderer() {
        return (listitem, o, i) -> {
            String currencyCode = (String) o;
            listitem.setLabel(currencyCode + " - " + configurationModel.getCurrencySymbol(currencyCode));
            listitem.setValue(currencyCode);
        };
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public String getSelectedCurrency() {
        return configurationModel.getCurrencyCode();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setSelectedCurrency(String currencyCode) {
        configurationModel.setCurrency(currencyCode);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public TypeOfWorkHours getPersonalTimesheetsTypeOfWorkHours() {
        return configurationModel.getPersonalTimesheetsTypeOfWorkHours();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setPersonalTimesheetsTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours) {
        configurationModel.setPersonalTimesheetsTypeOfWorkHours(typeOfWorkHours);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public TypeOfWorkHours getBudgetDefaultTypeOfWorkHours() {
        return configurationModel.getBudgetDefaultTypeOfWorkHours();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setBudgetDefaultTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours) {
        configurationModel.setBudgetDefaultTypeOfWorkHours(typeOfWorkHours);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Boolean getEnabledAutomaticBudget() {
        return configurationModel.getEnabledAutomaticBudget();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setEnabledAutomaticBudget(Boolean enabledAutomaticBudget) {
        configurationModel.setEnabledAutomaticBudget(enabledAutomaticBudget);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public List<PersonalTimesheetsPeriodicityEnum> getPersonalTimesheetsPeriodicities() {
        return Arrays.asList(PersonalTimesheetsPeriodicityEnum.values());
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public ListitemRenderer getPersonalTimesheetsPeriodicityRenderer() {
        return (listitem, o, i) -> {
            PersonalTimesheetsPeriodicityEnum periodicity = (PersonalTimesheetsPeriodicityEnum) o;
            listitem.setLabel(_(periodicity.getName()));
            listitem.setValue(periodicity);
        };
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public PersonalTimesheetsPeriodicityEnum getSelectedPersonalTimesheetsPeriodicity() {
        return configurationModel.getPersonalTimesheetsPeriodicity();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setSelectedPersonalTimesheetsPeriodicity(
            PersonalTimesheetsPeriodicityEnum personalTimesheetsPeriodicity) {

        configurationModel.setPersonalTimesheetsPeriodicity(personalTimesheetsPeriodicity);
    }

    private boolean isPersonalTimesheetsPeriodicityDisabled() {
        return configurationModel.isAnyPersonalTimesheetAlreadySaved();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public String getPersonalTimesheetsPeriodicityTooltip() {
        return isPersonalTimesheetsPeriodicityDisabled()
                ? _("Periocity cannot be changed because there is already any personal timesheet stored")
                : "";
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public Integer getSecondsPlanningWarning() {
        return configurationModel.getSecondsPlanningWarning();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setSecondsPlanningWarning(Integer secondsPlanningWarning) {
        configurationModel.setSecondsPlanningWarning(secondsPlanningWarning);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public String getRepositoryLocation() {
        return configurationModel.getRepositoryLocation();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setRepositoryLocation(String location) {
        configurationModel.setRepositoryLocation(location);
    }

    public List<Connector> getConnectors() {
        return configurationModel.getConnectors();
    }

    public Connector getSelectedConnector() {
        return selectedConnector;
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public void setSelectedConnector(Connector connector) {
        selectedConnector = connector;
        Util.reloadBindings(connectorPropertriesGrid);
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public List<ConnectorProperty> getConnectorPropertries() {
        return selectedConnector == null ? Collections.emptyList() : selectedConnector.getProperties();
    }

    /**
     * Used in configuration.zul
     * Should be public!
     */
    public RowRenderer getConnectorPropertriesRenderer() {
        return new RowRenderer() {
            @Override
            public void render(Row row, Object o, int i) throws Exception {
                ConnectorProperty property = (ConnectorProperty) o;
                row.setValue(property);

                Util.appendLabel(row, _(property.getKey()));

                if ("Protocol".equals(property.getKey())) {
                    appendValueCombobox(row, property);
                } else {
                    appendValueTextbox(row, property);
                }
            }

            private void appendValueTextbox(Row row, final ConnectorProperty property) {
                final Textbox textbox = new Textbox();
                textbox.setWidth("400px");
                textbox.setConstraint(checkPropertyValue(property));

                Util.bind(textbox, property::getValue, property::setValue);

                if (property.getKey().equals(PredefinedConnectorProperties.PASSWORD)
                        || property.getKey().equals(PredefinedConnectorProperties.EMAIL_PASSWORD)) {

                    textbox.setType("password");
                }

                // Need for method validateEmailFields()
                if (property.getKey().equals(PredefinedConnectorProperties.EMAIL_USERNAME)) {
                    emailUsernameTextbox = textbox;
                }

                if (property.getKey().equals(PredefinedConnectorProperties.EMAIL_PASSWORD)) {
                    emailPasswordTextbox = textbox;
                }

                if (property.getKey().equals(PredefinedConnectorProperties.EMAIL_SENDER)) {
                    emailSenderTextbox = textbox;
                }

                row.appendChild(textbox);
            }

            private void appendValueCombobox(Row row, final ConnectorProperty property) {

                final Combobox combobox = new Combobox();
                combobox.setWidth("400px");
                final List<String> protocols = new ArrayList<>();
                protocols.add("SMTP");
                protocols.add(STARTTLS_PROTOCOL);

                for (String item : protocols) {
                    Comboitem comboitem = new Comboitem();
                    comboitem.setValue(item);
                    comboitem.setLabel(item);
                    comboitem.setParent(combobox);

                    if ((!"".equals(property.getValue())) && (item.equals(property.getValue()))) {
                        combobox.setSelectedItem(comboitem);
                    }
                }

                combobox.addEventListener(Events.ON_SELECT, event -> {
                    if (combobox.getSelectedItem() != null) {
                        property.setValue(combobox.getSelectedItem().getValue().toString());
                    }
                });

                Util.bind(combobox, combobox::getSelectedItem, item -> {
                    if ((item != null) && (item.getValue() != null) && (item.getValue() instanceof String)) {
                        property.setValue(combobox.getSelectedItem().getValue().toString());
                    }
                });

                row.appendChild(combobox);

                // Needed for testing E-mail connection
                protocolsCombobox = combobox;
            }

            Constraint checkPropertyValue(final ConnectorProperty property) {
                final String key = property.getKey();

                return (comp, value) -> {
                    if (key.equals(PredefinedConnectorProperties.ACTIVATED)) {
                        if (!"Y".equalsIgnoreCase((String) value) && !"N".equalsIgnoreCase((String) value)) {
                            throw new WrongValueException(comp, _("Only {0} allowed", "Y/N"));
                        }
                    } else if (key.equals(PredefinedConnectorProperties.SERVER_URL)
                            || key.equals(PredefinedConnectorProperties.USERNAME)
                            || key.equals(PredefinedConnectorProperties.PASSWORD)
                            || key.equals(PredefinedConnectorProperties.JIRA_HOURS_TYPE)
                            || key.equals(PredefinedConnectorProperties.HOST)
                            || key.equals(PredefinedConnectorProperties.PORT)
                            || key.equals(PredefinedConnectorProperties.EMAIL_SENDER)
                            || key.equals(PredefinedConnectorProperties.PROTOCOL)) {

                        ((InputElement) comp).setConstraint("no empty:" + _("cannot be empty"));

                    } else if (key.equals(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET)
                            || key.equals(PredefinedConnectorProperties.TIM_NR_DAYS_ROSTER)
                            || key.equals(PredefinedConnectorProperties.PORT)) {

                        if (!isNumeric((String) value)) {
                            throw new WrongValueException(comp, _("Only digits allowed"));
                        }
                    }
                };
            }

            private boolean isNumeric(String input) {
                try {
                    Integer.parseInt(input);
                    return true;
                } catch (NumberFormatException e) {
                    return false;
                }
            }

        };
    }

    private boolean areEmailFieldsValid() {
        if (protocolsCombobox != null && protocolsCombobox.getSelectedItem() != null) {

            boolean isNotNullValue = emailUsernameTextbox.getValue() != null
                    && emailPasswordTextbox.getValue() != null && emailUsernameTextbox.getValue().length() != 0
                    && emailPasswordTextbox.getValue().length() != 0;

            if (STARTTLS_PROTOCOL.equals(protocolsCombobox.getSelectedItem().getLabel()) && isNotNullValue
                    && emailSenderTextbox.getValue().matches("^\\S+@\\S+\\.\\S+$")) {

                return true;
            }

            if (protocolsCombobox.getSelectedItem() != null
                    && "SMTP".equals(protocolsCombobox.getSelectedItem().getLabel())) {

                return true;
            }
        }

        return false;
    }

    /**
     * Upload image to classes folder via ZK Fileupload.
     *
     * Used in configuration.zul
     * Should be public!
     *
     * @param media
     */
    public void importLogo(Media media) {

        if (Util.logo != null) {
            /* We are going to overwrite existing logo */
            removeLogo();
        }

        if (Util.logo == null) {
            if (checkFormat(media.getFormat())) {
                BufferedInputStream in;
                BufferedOutputStream out = null;
                File fileToSave;

                InputStream inputStream = media.getStreamData();
                in = new BufferedInputStream(inputStream);

                try {
                    fileToSave = new File(ContextLoaderListener.getCurrentWebApplicationContext().getResource("/")
                            .getFile().getPath() + "\\" + media.getName());

                    OutputStream outputStream = new FileOutputStream(fileToSave);
                    out = new BufferedOutputStream(outputStream);

                    byte[] buffer = new byte[1024];
                    int ch = in.read(buffer);

                    while (ch != -1) {
                        out.write(buffer, 0, ch);
                        ch = in.read(buffer);
                    }

                } catch (IOException ignored) {
                }

                finally {
                    try {
                        if (out != null)
                            out.close();

                        in.close();
                    } catch (IOException ignored) {
                    }
                }

                Util.setLogoFromTarget(media.getName());
                configurationModel.setCompanyLogoURL(media.getName());
                ((Textbox) configurationWindow.getFellow("companyLogoURL")).setValue(media.getName());
                ((org.zkoss.zul.Image) configurationWindow.getFellow(LOGO_PREVIEW_COMPONENT)).setContent(Util.logo);

            } else {
                messages.showMessage(Level.WARNING, _("The only current supported formats are png and jpeg"));
            }
        }
    }

    private boolean checkFormat(String format) {
        /* http://stackoverflow.com/questions/23424399/jpg-vs-jpeg-image-formats */
        return format.matches("(?i).*png") || format.matches("(?i).*jpeg") || format.matches("(?i).*jpg");
    }

    /**
     * Handler of remove logo button.
     *
     * Should be public!
     */
    public void removeLogo() {
        if (!"".equals(companyLogoURL.getValue())) {
            ((org.zkoss.zul.Image) configurationWindow.getFellow(LOGO_PREVIEW_COMPONENT)).setSrc("");
            findAndRemoveLogoFromTarget(companyLogoURL.getValue());
            Util.logo = null;
        }

        companyLogoURL.setValue("");
        configurationModel.setCompanyLogoURL("");
    }

    /**
     * Setting preview image.
     *
     * Used in configuration.zul
     * Should be public!
     */
    public void setPreviewLogo() {
        if (!"".equals(companyLogoURL.getValue())) {
            ((org.zkoss.zul.Image) configurationWindow.getFellow(LOGO_PREVIEW_COMPONENT)).setContent(Util.logo);
        }
    }

    /**
     * Trying to delete file from classes folder.
     */
    private void findAndRemoveLogoFromTarget(String name) {
        File fileToDelete;
        try {
            fileToDelete = ContextLoaderListener.getCurrentWebApplicationContext().getResource(name).getFile();
            fileToDelete.delete();
        } catch (IOException ignored) {
        }
    }
}