/** * */ package net.frontlinesms.ui.handler.email; import javax.mail.MessagingException; import net.frontlinesms.FrontlineSMS; import net.frontlinesms.FrontlineUtils; import net.frontlinesms.data.DuplicateKeyException; import net.frontlinesms.data.domain.EmailAccount; import net.frontlinesms.data.repository.EmailAccountDao; import net.frontlinesms.email.EmailUtils; import net.frontlinesms.ui.FrontlineUI; import net.frontlinesms.ui.ThinletUiEventHandler; import net.frontlinesms.ui.UiGeneratorController; import net.frontlinesms.ui.i18n.InternationalisationUtils; import net.frontlinesms.ui.i18n.TextResourceKeyOwner; import org.apache.log4j.Logger; /** * Dialog handler for adding/editing Email accounts * @author Morgan Belkadi <morgan@frontlinesms.com> * @author Alex Anderson <alex@frontlinesms.com> */ @TextResourceKeyOwner(prefix="I18N_") public class EmailAccountSettingsDialogHandler implements ThinletUiEventHandler { //> STATIC CONSTANTS //> THINLET LAYOUT DEFINITION FILES /** UI XML File Path: This is the outline for the dialog */ private static final String UI_FILE_EMAIL_ACCOUNT_FORM = "/ui/core/email/dgAccountSettings.xml"; private static final String UI_FILE_CONNECTION_WARNING_FORM = "/ui/core/email/dgConnectionWarning.xml"; private static final String UI_FILE_CONNECTION_WARNING_ERROR_MESSAGE = "/ui/core/email/dgConnectionWarningError.xml"; //> THINLET COMPONENT NAMES private static final String UI_COMPONENT_CB_USE_SSL = "cbUseSSL"; private static final String UI_COMPONENT_LB_EXAMPLE_PORT = "lbPortExample"; private static final String UI_COMPONENT_LB_EXAMPLE_SERVER = "lbServerExample"; private static final String UI_COMPONENT_LK_HELP = "lkHelp"; private static final String UI_COMPONENT_RB_IMAP = "rbImap"; private static final String UI_COMPONENT_RB_POP = "rbPop"; private static final String UI_COMPONENT_PN_POP_IMAP = "pnPopImap"; private static final String UI_COMPONENT_TA_ERROR_MESSAGE = "taErrorMessage"; private static final String UI_COMPONENT_TF_ACCOUNT = "tfAccount"; private static final String UI_COMPONENT_TF_ACCOUNT_PASS = "tfAccountPass"; private static final String UI_COMPONENT_TF_ACCOUNT_SERVER_PORT = "tfPort"; private static final String UI_COMPONENT_TF_MAIL_SERVER = "tfMailServer"; //> I18N TEXT KEYS private static final String I18N_ACCOUNT_NAME_BLANK = "message.account.name.blank"; private static final String I18N_ACCOUNT_NAME_ALREADY_EXISTS = "message.account.already.exists"; private static final String I18N_COMMON_EMAIL_ACCOUNT_SETTINGS = "common.email.account.settings"; private static final String I18N_EDITING_EMAIL_ACCOUNT = "common.editing.email.account"; private static final String I18N_MMS_EMAIL_ACCOUNT_SETTINGS = "mms.email.account.settings"; private static final String I18N_NEW_ACCOUNT = "common.new.account"; private static final String EXAMPLE_PORT_POP = "email.account.example.port.pop"; private static final String EXAMPLE_PORT_SMTP = "email.account.example.port.smtp"; private static final String EXAMPLE_SERVER_POP = "email.account.example.server.pop"; private static final String EXAMPLE_SERVER_SMTP = "email.account.example.server.smtp"; private static final int DEFAULT_PORT_POP = 110; private static final int DEFAULT_PORT_SMTP = 25; //> INSTANCE PROPERTIES /** Logging object */ private final Logger log = FrontlineUtils.getLogger(this.getClass()); /** The {@link UiGeneratorController} that shows the tab. */ private final UiGeneratorController ui; private Object dialogComponent; private boolean isForReceiving; private EmailAccountDao emailAccountDao; private EmailAccount originalEmailAccount; private String connectionWarningMessage; //> CONSTRUCTORS /** * Create a new instance of this controller. * @param uiController * @param notification the notification which triggered this dialog; used to determine what to display in the dialog */ public EmailAccountSettingsDialogHandler(UiGeneratorController uiController, boolean isForReceiving) { this.ui = uiController; this.isForReceiving = isForReceiving; FrontlineSMS frontlineController = ui.getFrontlineController(); this.emailAccountDao = frontlineController.getEmailAccountFactory(); } //> ACCESSORS //> UI SHOW METHODS public Object getDialog() { return this.dialogComponent; } /** * Setup the details of the dialog. */ public void initDialog(EmailAccount emailAccount) { this.dialogComponent = ui.loadComponentFromFile(UI_FILE_EMAIL_ACCOUNT_FORM, this); this.originalEmailAccount = emailAccount; Object tfPort = ui.find(dialogComponent, UI_COMPONENT_TF_ACCOUNT_SERVER_PORT); this.ui.setSelected(find(UI_COMPONENT_RB_IMAP), true); this.ui.setSelected(find(UI_COMPONENT_RB_POP), false); if (this.isForReceiving) { ui.setText(find(UI_COMPONENT_LB_EXAMPLE_PORT), InternationalisationUtils.getI18nString(EXAMPLE_PORT_POP)); ui.setText(find(UI_COMPONENT_LB_EXAMPLE_SERVER), InternationalisationUtils.getI18nString(EXAMPLE_SERVER_POP)); ui.setText(tfPort, String.valueOf(DEFAULT_PORT_POP)); this.ui.setText(dialogComponent, InternationalisationUtils.getI18nString(I18N_MMS_EMAIL_ACCOUNT_SETTINGS)); if (this.originalEmailAccount != null && this.originalEmailAccount.getProtocol().equals(EmailUtils.POP3)) { this.ui.setSelected(find(UI_COMPONENT_RB_IMAP), false); this.ui.setSelected(find(UI_COMPONENT_RB_POP), true); } } else { ui.setText(dialogComponent, InternationalisationUtils.getI18nString(I18N_NEW_ACCOUNT)); ui.setText(find(UI_COMPONENT_LB_EXAMPLE_PORT), InternationalisationUtils.getI18nString(EXAMPLE_PORT_SMTP)); ui.setText(find(UI_COMPONENT_LB_EXAMPLE_SERVER), InternationalisationUtils.getI18nString(EXAMPLE_SERVER_SMTP)); ui.setText(tfPort, String.valueOf(DEFAULT_PORT_SMTP)); this.ui.setText(dialogComponent, InternationalisationUtils.getI18nString(I18N_COMMON_EMAIL_ACCOUNT_SETTINGS)); } this.ui.setVisible(find(UI_COMPONENT_PN_POP_IMAP), isForReceiving); this.ui.setVisible(find(UI_COMPONENT_LK_HELP), isForReceiving); if (emailAccount != null) { ui.setText(dialogComponent, InternationalisationUtils.getI18nString(I18N_EDITING_EMAIL_ACCOUNT, emailAccount.getAccountName())); this.populatePanel(); } } /** Populate the panel containing settings specific to the currently-selected {@link EmailAccount}. */ private void populatePanel() { Object tfServer = ui.find(dialogComponent, UI_COMPONENT_TF_MAIL_SERVER); Object tfAccountName = ui.find(dialogComponent, UI_COMPONENT_TF_ACCOUNT); Object tfPassword = ui.find(dialogComponent, UI_COMPONENT_TF_ACCOUNT_PASS); Object cbUseSSL = ui.find(dialogComponent, UI_COMPONENT_CB_USE_SSL); Object tfPort = ui.find(dialogComponent, UI_COMPONENT_TF_ACCOUNT_SERVER_PORT); ui.setText(tfServer, this.originalEmailAccount.getAccountServer()); ui.setText(tfAccountName, this.originalEmailAccount.getAccountName()); ui.setText(tfPassword, this.originalEmailAccount.getAccountPassword()); ui.setSelected(cbUseSSL, this.originalEmailAccount.useSsl()); ui.setText(tfPort, String.valueOf(this.originalEmailAccount.getAccountServerPort())); } private Object find(String componentName) { return ui.find(this.dialogComponent, componentName); } //> UI EVENT METHODS /** * After failing to connect to the email server, the user has an option to * create the account anyway. This method handles this action. * * @param currentDialog */ public void createAccount(Object currentDialog) { log.trace("ENTER"); ui.removeDialog(currentDialog); log.debug("Creating account anyway!"); Object accountDialog = ui.getAttachedObject(currentDialog); String server = ui.getText(ui.find(accountDialog, UI_COMPONENT_TF_MAIL_SERVER)); String accountName = ui.getText(ui.find(accountDialog, UI_COMPONENT_TF_ACCOUNT)); String password = ui.getText(ui.find(accountDialog, UI_COMPONENT_TF_ACCOUNT_PASS)); boolean useSSL = ui.isSelected(ui.find(accountDialog, UI_COMPONENT_CB_USE_SSL)); String portAsString = ui.getText(ui.find(accountDialog, UI_COMPONENT_TF_ACCOUNT_SERVER_PORT)); int serverPort; try { serverPort = Integer.parseInt(portAsString); } catch (NumberFormatException e1) { if (useSSL) serverPort = EmailAccount.DEFAULT_SMTPS_PORT; else serverPort = EmailAccount.DEFAULT_SMTP_PORT; } log.debug("Server Name [" + server + "]"); log.debug("Account Name [" + accountName + "]"); log.debug("Account Server Port [" + serverPort + "]"); log.debug("SSL [" + useSSL + "]"); try { if (this.originalEmailAccount == null) { // Then it's a new account EmailAccount acc = new EmailAccount(accountName, server, serverPort, password, useSSL, this.isForReceiving, getCurrentProtocol()); emailAccountDao.saveEmailAccount(acc); log.debug("Account [" + acc.getAccountName() + "] created!"); } else { // Then we're editing the account this.originalEmailAccount.setAccountName(accountName); this.originalEmailAccount.setAccountServer(server); this.originalEmailAccount.setAccountServerPort(serverPort); this.originalEmailAccount.setAccountPassword(password); this.originalEmailAccount.setUseSSL(useSSL); this.originalEmailAccount.setProtocol(getCurrentProtocol()); // We're not setting the isForReceiving flag, as it must never change emailAccountDao.updateEmailAccount(originalEmailAccount); log.debug("Account [" + accountName + "] updated!"); } } catch (DuplicateKeyException e) { log.debug("Account already exists", e); ui.alert(InternationalisationUtils.getI18nString(I18N_ACCOUNT_NAME_ALREADY_EXISTS)); log.trace("EXIT"); return; } log.trace("EXIT"); this.removeDialog(dialogComponent); } private String getCurrentProtocol() { return (!this.isForReceiving ? EmailUtils.SMTP : (ui.isSelected(ui.find(UI_COMPONENT_RB_POP)) ? EmailUtils.POP3 : EmailUtils.IMAP)); } /** * This method is called when the save button is pressed in the new mail account dialog. * @param dialog */ public void saveEmailAccount(Object dialog) { log.trace("ENTER"); String server = ui.getText(find(UI_COMPONENT_TF_MAIL_SERVER)); String accountName = ui.getText(find(UI_COMPONENT_TF_ACCOUNT)); String password = ui.getText(find(UI_COMPONENT_TF_ACCOUNT_PASS)); boolean useSSL = ui.isSelected(find(UI_COMPONENT_CB_USE_SSL)); String portAsString = ui.getText(find(UI_COMPONENT_TF_ACCOUNT_SERVER_PORT)); String protocol = getCurrentProtocol(); int serverPort; try { serverPort = Integer.parseInt(portAsString); } catch (NumberFormatException e1) { if (useSSL) serverPort = EmailAccount.DEFAULT_SMTPS_PORT; else serverPort = EmailAccount.DEFAULT_SMTP_PORT; } log.debug("Server [" + server + "]"); log.debug("Account [" + accountName + "]"); log.debug("Account Server Port [" + serverPort + "]"); log.debug("SSL [" + useSSL + "]"); if (accountName.equals("")) { ui.alert(InternationalisationUtils.getI18nString(I18N_ACCOUNT_NAME_BLANK)); log.trace("EXIT"); return; } try { log.debug("Testing connection to [" + server + "]"); EmailUtils.testConnection(isForReceiving, server, accountName, serverPort, password, useSSL, protocol); // Exception catched if failed log.debug("Connection was successful, creating account [" + accountName + "]"); if (this.originalEmailAccount == null) { EmailAccount account = new EmailAccount(accountName, server, serverPort, password, useSSL, this.isForReceiving, protocol); emailAccountDao.saveEmailAccount(account); } else { this.originalEmailAccount.setAccountName(accountName); this.originalEmailAccount.setAccountServer(server); this.originalEmailAccount.setAccountServerPort(serverPort); this.originalEmailAccount.setAccountPassword(password); this.originalEmailAccount.setUseSSL(useSSL); this.originalEmailAccount.setProtocol(protocol); // We're not setting the isForReceiving flag, as it must never change emailAccountDao.updateEmailAccount(this.originalEmailAccount); } ui.removeDialog(this.dialogComponent); } catch (MessagingException e) { log.info("Fail to connect to server [" + server + "]"); log.debug("Fail to connect to server [" + server + "]", e); this.connectionWarningMessage = e.getMessage(); if (e.getNextException() != null) { this.connectionWarningMessage += e.getNextException(); } this.showConnectionWarningDialog(dialog); } catch (DuplicateKeyException e) { log.debug(InternationalisationUtils.getI18nString(I18N_ACCOUNT_NAME_ALREADY_EXISTS), e); ui.alert(InternationalisationUtils.getI18nString(I18N_ACCOUNT_NAME_ALREADY_EXISTS)); } log.trace("EXIT"); } private void showConnectionWarningDialog(Object dialog) { Object connectWarning = ui.loadComponentFromFile(UI_FILE_CONNECTION_WARNING_FORM, this); ui.setAttachedObject(connectWarning, dialog); ui.add(connectWarning); } /** @param dialog the dialog to remove * @see UiGeneratorController#remove(Object) */ public void removeDialog(Object dialog) { this.ui.closeDeviceConnectionDialog(dialog); } public void showDetails () { Object errorPanel = ui.loadComponentFromFile(UI_FILE_CONNECTION_WARNING_ERROR_MESSAGE, this); ui.setText(this.ui.find(errorPanel, UI_COMPONENT_TA_ERROR_MESSAGE), this.connectionWarningMessage); ui.add(errorPanel); } /** * Opens a page of the help manual * @see FrontlineUI#showHelpPage(String) */ public void showHelpPage(String page) { this.ui.showHelpPage(page); } }