org.sipfoundry.sipxconfig.userportal.client.UserPhonebookSearch.java Source code

Java tutorial

Introduction

Here is the source code for org.sipfoundry.sipxconfig.userportal.client.UserPhonebookSearch.java

Source

/*
 *
 *
 * Copyright (C) 2009 Pingtel Corp., certain elements licensed under a Contributor Agreement.
 * Contributors retain copyright to elements licensed under a Contributor Agreement.
 * Licensed to the User under the LGPL license.
 *
 */
package org.sipfoundry.sipxconfig.userportal.client;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.Timer;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.Cursor;
import com.smartgwt.client.types.OperatorId;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.Side;
import com.smartgwt.client.types.Visibility;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.util.StringUtil;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Img;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.events.CloseClientEvent;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.ValuesManager;
import com.smartgwt.client.widgets.form.fields.CheckboxItem;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.PasswordItem;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.Tab;
import com.smartgwt.client.widgets.tab.TabSet;

import org.sipfoundry.sipxconfig.userportal.locale.SearchConstants;
import org.sipfoundry.sipxconfig.userportal.widget.PagedPhonebookDataSource;
import org.sipfoundry.sipxconfig.userportal.widget.PhonebookDataSource;

// FIXME: all sizes should be in CSS
public class UserPhonebookSearch implements EntryPoint {

    private static final String DUMMY_ID = "-1";
    private static final String EMPTY_STRING = "";
    private static final String WILD_CARD = "*";
    private static final int CLICK_TO_CALL_ERROR = 400;
    private static final String VARIANT = "application/json";
    private static final String CONTENT_TYPE = "Content-Type";

    private static final String[] FIELDS_PHONENUMBERS = { PhonebookDataSource.NUMBER,
            PhonebookDataSource.CELL_PHONE_NUMBER, PhonebookDataSource.HOME_PHONE_NUMBER,
            PhonebookDataSource.ASSISTANT_PHONE_NUMBER };

    private static SearchConstants s_searchConstants = GWT.create(SearchConstants.class);

    @Override
    public void onModuleLoad() {

        final PageBrowseLayout pageBrowseLayout = new PageBrowseLayout();
        final CheckboxItem showOnPhone = new CheckboxItem(s_searchConstants.showPersonalContactsOnPhone());
        showOnPhone.setVisible(false);

        final DynamicForm showOnPhoneForm = new DynamicForm();
        showOnPhoneForm.setFields(showOnPhone);

        final Label googleDomain = new Label();

        final DataSource phonebookDS = new PagedPhonebookDataSource("phonebookGridId") {
            @Override
            protected void onDataFetch(Integer size, String showStatus, String domain) {
                pageBrowseLayout.update(size, this);
                if (showStatus.equalsIgnoreCase("n/a")) {
                    showOnPhone.hide();
                } else {
                    showOnPhone.show();
                    showOnPhone.setValue(Boolean.valueOf(showStatus));
                }
                googleDomain.setContents(s_searchConstants.defaultGoogleDomain() + domain);
            }
        };

        showOnPhone.addChangedHandler(new ChangedHandler() {
            @Override
            public void onChanged(ChangedEvent event) {
                updateShowOnPhoneStatus(showOnPhone.getValueAsBoolean(),
                        s_searchConstants.showPersonalContactsMessage());
            }
        });

        final PhonebookGrid phonebookGrid = new PhonebookGrid(phonebookDS);
        final Details details = new Details(phonebookGrid);
        details.addData();

        final Details detailsLayout = new Details(phonebookGrid, 6, 6, 6);
        detailsLayout.addData();

        phonebookGrid.addRecordClickHandler(new RecordClickHandler() {
            @Override
            public void onRecordClick(RecordClickEvent event) {
                int recordIndex = event.getRecordNum();
                phonebookGrid.collapeAllRecordsAndExpandThisOne(phonebookGrid.getRecord(recordIndex), recordIndex);
            }
        });

        final SearchForm searchForm = new SearchForm(phonebookDS);
        searchForm.addHandler(phonebookGrid);

        pageBrowseLayout.setup(phonebookGrid, phonebookDS, searchForm);

        IButton gmailImport = new IButton(s_searchConstants.gmailImportTitle());
        gmailImport.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                final GmailImportModalWindow gmailModal = new GmailImportModalWindow(phonebookGrid);
                gmailModal.show();
            }
        });
        gmailImport.setAutoFit(true);

        IButton deletePrivatePhonebook = new IButton(s_searchConstants.deletePrivatePhonebookTitle());
        deletePrivatePhonebook.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(ClickEvent event) {
                SC.ask(s_searchConstants.confirmDeletePrivatePhonebook(), new BooleanCallback() {
                    @Override
                    public void execute(Boolean value) {
                        if (value != null && value) {
                            String url = "/sipxconfig/rest/my/phonebook";
                            HttpRequestBuilder.doDelete(url, s_searchConstants.deletePrivatePhonebookSuccess());
                            phonebookGrid.refreshRecordsWithDelay();
                        }
                    }
                });
            }
        });
        deletePrivatePhonebook.setAutoFit(true);

        TabSet tabSet = new TabSet();
        tabSet.setTabBarPosition(Side.TOP);
        tabSet.setWidth100();
        tabSet.setHeight100();

        Tab entriesTab = new Tab(s_searchConstants.entries());

        VLayout searchLayout = new VLayout();
        searchLayout.setMargin(5);
        searchLayout.addMember(searchForm);
        searchLayout.addMember(showOnPhoneForm);

        HLayout leftHLayout = new HLayout();
        leftHLayout.setMargin(10);
        leftHLayout.addMember(searchLayout);
        leftHLayout.setWidth("30%");
        leftHLayout.setAlign(Alignment.LEFT);

        HLayout rightHLayout = new HLayout();
        rightHLayout.setMargin(10);
        rightHLayout.addMember(googleDomain);
        rightHLayout.addMember(gmailImport);
        rightHLayout.addMember(deletePrivatePhonebook);
        rightHLayout.setWidth(WILD_CARD);
        rightHLayout.setAlign(Alignment.RIGHT);

        HLayout topHLayout = new HLayout();
        topHLayout.addMember(leftHLayout);
        topHLayout.addMember(rightHLayout);
        topHLayout.setWidth100();
        topHLayout.setHeight(30);

        VLayout layout = new VLayout(5);
        layout.setMargin(5);
        layout.addMember(topHLayout);
        layout.addMember(phonebookGrid);
        layout.addMember(pageBrowseLayout);
        entriesTab.setPane(layout);

        Tab newContactTab = new Tab(s_searchConstants.addNewContact());
        newContactTab.setPane(new DetailsLayout(detailsLayout, phonebookGrid));

        tabSet.addTab(entriesTab);
        tabSet.addTab(newContactTab);

        tabSet.draw();

    }

    private static class PageBrowseLayout extends HLayout {

        private final IButton m_nextPage = new IButton(s_searchConstants.nextPage());
        private final IButton m_previousPage = new IButton(s_searchConstants.previousPage());

        public PageBrowseLayout() {
            m_nextPage.setDisabled(true);
            m_previousPage.setDisabled(true);

            addMember(m_previousPage);
            addMember(m_nextPage);
        }

        public void setup(final PhonebookGrid phonebookGrid, final DataSource phonebookDS,
                final SearchForm searchForm) {

            m_nextPage.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    if (phonebookDS instanceof PagedPhonebookDataSource) {
                        PagedPhonebookDataSource pagedPhonebookDS = (PagedPhonebookDataSource) phonebookDS;
                        pagedPhonebookDS.setPageNumber(pagedPhonebookDS.getPageNumber() + 1);
                        searchForm.clearValues();
                        phonebookGrid.refreshPhonebook();
                    }
                }
            });

            m_previousPage.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    if (phonebookDS instanceof PagedPhonebookDataSource) {
                        PagedPhonebookDataSource pagedPhonebookDS = (PagedPhonebookDataSource) phonebookDS;
                        pagedPhonebookDS.setPageNumber(pagedPhonebookDS.getPageNumber() - 1);
                        searchForm.clearValues();
                        phonebookGrid.refreshPhonebook();
                    }
                }
            });
        }

        public void update(Integer size, DataSource phonebookDS) {

            if (phonebookDS instanceof PagedPhonebookDataSource) {
                PagedPhonebookDataSource pagedPhonebookDS = (PagedPhonebookDataSource) phonebookDS;
                int currentPageNum = pagedPhonebookDS.getPageNumber();

                m_nextPage.setDisabled(size <= PagedPhonebookDataSource.PAGE_SIZE * currentPageNum);
                m_previousPage.setDisabled(1 == currentPageNum);
            }
        }
    }

    private static class PhonebookGrid extends ListGrid {
        private static final String NRML_TABLE_FIELD_WIDTH = "20%";
        private ListGridRecord m_expandedRecord;
        private int m_expandedRecordIndex = Integer.valueOf(DUMMY_ID);

        public PhonebookGrid(DataSource phonebookDS) {
            ListGridField firstNameField = new ListGridField(PhonebookDataSource.FIRST_NAME,
                    s_searchConstants.firstName());
            ListGridField lastNameField = new ListGridField(PhonebookDataSource.LAST_NAME,
                    s_searchConstants.lastName());
            ListGridField numberField = new ListGridField(PhonebookDataSource.NUMBER, s_searchConstants.number());
            ListGridField emailField = new ListGridField(PhonebookDataSource.EMAIL_ADDRESS,
                    s_searchConstants.emailAddress());

            firstNameField.setWidth(NRML_TABLE_FIELD_WIDTH);
            firstNameField.setCellFormatter(getHtmlCellFormatter());

            lastNameField.setWidth(NRML_TABLE_FIELD_WIDTH);
            lastNameField.setCellFormatter(getHtmlCellFormatter());

            numberField.setWidth(NRML_TABLE_FIELD_WIDTH);
            emailField.setWidth(WILD_CARD);

            setEmptyMessage(s_searchConstants.noUserFound());
            setWidth100();
            setHeight100();

            setBodyOverflow(Overflow.AUTO);
            setOverflow(Overflow.VISIBLE);
            setAlternateRecordStyles(true);
            setDataSource(phonebookDS);
            setAutoFetchData(true);
            setLoadingDataMessage(s_searchConstants.loadingDataPrompt());
            setLoadingMessage(s_searchConstants.loadingDataPrompt());

            setCellHeight(25);
            setHeaderHeight(35);
            setFields(firstNameField, lastNameField, numberField, emailField);

            setCanExpandRecords(true);
        }

        public void collapeAllRecordsAndExpandThisOne(ListGridRecord record, int recordIndex) {
            if (null != m_expandedRecord) {
                collapseRecord(m_expandedRecord);
            }

            if (m_expandedRecordIndex != recordIndex) {
                expandRecord(record);
                m_expandedRecord = record;
                m_expandedRecordIndex = recordIndex;
            } else {
                resetExpandedRowIndex();
            }
            this.scrollToRow(recordIndex);
        }

        public void refreshPhonebook() {
            if (null != m_expandedRecord) {
                collapseRecord(m_expandedRecord);
            }
            resetExpandedRowIndex();
            invalidateCache();
        }

        public void refreshRecordsWithDelay() {
            setOpacity(50);
            // fetch data with 1.5 secs delay so the changes to be picked up
            Timer refreshTimer = new Timer() {
                @Override
                public void run() {
                    refreshPhonebook();
                    setOpacity(null);
                }
            };
            refreshTimer.schedule(1500);
        }

        @Override
        protected Canvas getExpansionComponent(final ListGridRecord record) {
            return new ExpandedContactComponent(this);
        }

        private void resetExpandedRowIndex() {
            m_expandedRecord = null;
            m_expandedRecordIndex = Integer.valueOf(DUMMY_ID);
        }

        private CellFormatter getHtmlCellFormatter() {
            return new CellFormatter() {
                @Override
                public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                    return StringUtil.asHTML((String) value);
                }
            };
        }
    }

    private static class SearchForm extends DynamicForm {
        private final TextItem m_firstName;
        private final TextItem m_lastName;
        private final TextItem m_number;
        private final TextItem m_email;
        private final TextItem m_fullName;

        public SearchForm(DataSource phonebookDS) {
            m_firstName = new TextItem();
            m_firstName.setCriteriaField(PhonebookDataSource.FIRST_NAME);
            m_firstName.setOperator(OperatorId.ISTARTS_WITH);
            m_firstName.setTitle(s_searchConstants.searchWidgetTitle());

            m_lastName = new TextItem();
            m_lastName.setCriteriaField(PhonebookDataSource.LAST_NAME);
            m_lastName.setOperator(OperatorId.ISTARTS_WITH);
            m_lastName.setVisible(false);

            m_number = new TextItem();
            m_number.setCriteriaField(PhonebookDataSource.NUMBER);
            m_number.setOperator(OperatorId.ISTARTS_WITH);
            m_number.setVisible(false);

            m_email = new TextItem();
            m_email.setCriteriaField(PhonebookDataSource.EMAIL_ADDRESS);
            m_email.setOperator(OperatorId.ISTARTS_WITH);
            m_email.setVisible(false);

            m_fullName = new TextItem();
            m_fullName.setCriteriaField(PhonebookDataSource.FULL_NAME);
            m_fullName.setOperator(OperatorId.ISTARTS_WITH);
            m_fullName.setVisible(false);

            setFields(m_firstName, m_lastName, m_number, m_email, m_fullName);
            setDataSource(phonebookDS);
            setOperator(OperatorId.OR);
        }

        void addHandler(final ListGrid phonebookGrid) {
            ChangedHandler onChange = new ChangedHandler() {
                @Override
                public void onChanged(ChangedEvent event) {
                    m_lastName.setValue((String) m_firstName.getValue());
                    m_number.setValue((String) m_firstName.getValue());
                    m_email.setValue((String) m_firstName.getValue());
                    m_fullName.setValue((String) m_firstName.getValue());
                    if (m_firstName.getValue() != null) {
                        phonebookGrid.filterData(getValuesAsCriteria());
                    } else {
                        phonebookGrid.filterData();
                    }
                }
            };
            m_firstName.addChangedHandler(onChange);
        }
    }

    private static final class ExpandedContactComponent extends VLayout {
        private final IButton m_editButton = new IButton(s_searchConstants.edit());
        private final IButton m_saveButton = new IButton(s_searchConstants.save());
        private final IButton m_resetButton = new IButton(s_searchConstants.reset());
        private final IButton m_cancelButton = new IButton(s_searchConstants.cancel());

        public ExpandedContactComponent(final PhonebookGrid grid) {
            setStyleName("gwtExpandedContact");
            setWidth("95%");

            final ListGridRecord record = grid.getSelectedRecord();

            final Details details = new Details(grid);
            details.setData(record);

            final String entryId = record.getAttributeAsString(PhonebookDataSource.ENTRY_ID);

            IButton removeButton = new IButton(s_searchConstants.delete());
            removeButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    SC.ask(s_searchConstants.confirmDelete(), new BooleanCallback() {
                        @Override
                        public void execute(Boolean value) {
                            if (value != null && value) {
                                ((PhonebookDataSource) grid.getDataSource()).deleteEntry(entryId,
                                        s_searchConstants.deleteContactSuccessMessage());
                                grid.refreshRecordsWithDelay();
                            }
                        }
                    });
                }
            });

            hideEditControls();

            m_editButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    details.editData();
                    showEditControls();
                }
            });

            m_saveButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    details.onSaveClick(entryId);
                }
            });

            m_resetButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    details.onResetClick();
                }
            });

            m_cancelButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    details.onCancelClick();
                    hideEditControls();
                }
            });

            HLayout formActions = new HLayout();
            formActions.setMembersMargin(4);
            formActions.setAlign(Alignment.RIGHT);
            formActions.setPadding(10);

            if (!entryId.equals(DUMMY_ID)) {
                formActions.addMember(m_saveButton);
                formActions.addMember(m_resetButton);
                formActions.addMember(m_cancelButton);

                formActions.addMember(m_editButton);
                formActions.addMember(removeButton);
            }

            addMember(new DetailsTab(details));
            addMember(formActions);

        }

        private final void showEditControls() {
            m_saveButton.setVisibility(Visibility.INHERIT);
            m_resetButton.setVisibility(Visibility.INHERIT);
            m_cancelButton.setVisibility(Visibility.INHERIT);
            m_editButton.setVisibility(Visibility.HIDDEN);
        }

        private final void hideEditControls() {
            m_saveButton.setVisibility(Visibility.HIDDEN);
            m_resetButton.setVisibility(Visibility.HIDDEN);
            m_cancelButton.setVisibility(Visibility.HIDDEN);
            m_editButton.setVisibility(Visibility.INHERIT);
        }
    }

    private static final class DetailsTab extends TabSet {
        public DetailsTab(Details details) {
            setOverflow(Overflow.VISIBLE);
            setHeight("260px");

            HLayout generalLayout = new HLayout();
            generalLayout.addMember(details.getAvatarSection());
            generalLayout.addMember(details.getGeneralForm());
            generalLayout.setOverflow(Overflow.VISIBLE);
            generalLayout.setAutoHeight();
            generalLayout.setAutoWidth();

            HLayout addressLayout = new HLayout();
            addressLayout.addMember(details.getHomeForm());
            addressLayout.addMember(details.getOfficeForm());
            addressLayout.setMembersMargin(10);
            addressLayout.setOverflow(Overflow.VISIBLE);
            addressLayout.setWidth100();
            addressLayout.setHeight100();

            Tab generalTab = new Tab();
            generalTab.setPane(generalLayout);
            generalTab.setTitle(s_searchConstants.generalForm());

            Tab homeTab = new Tab();
            homeTab.setPane(addressLayout);
            homeTab.setTitle(s_searchConstants.address());

            addTab(generalTab);
            addTab(homeTab);
        }
    }

    private static final class DetailsLayout extends VLayout {
        public DetailsLayout(final Details details, final PhonebookGrid grid) {
            final IButton saveButton = new IButton(s_searchConstants.save());
            saveButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    details.onSaveClick(DUMMY_ID);
                }
            });

            Label title = new Label(s_searchConstants.newContact());
            title.setStyleName("gwtHeading");
            title.setAutoHeight();
            title.setWidth100();
            title.setAlign(Alignment.CENTER);

            addMember(title);
            addMember(details.getGeneralForm());
            addMember(details.getHomeForm());
            addMember(details.getOfficeForm());
            addMember(saveButton);

            setMembersMargin(10);
        }
    }

    private static class Details {
        private static final String[] FIELDS_GENERAL = { PhonebookDataSource.FIRST_NAME, PhonebookDataSource.NUMBER,
                PhonebookDataSource.EMAIL_ADDRESS, PhonebookDataSource.LAST_NAME, PhonebookDataSource.LOCATION,
                PhonebookDataSource.ALTERNATE_EMAIL_ADDRESS, PhonebookDataSource.JOB_TITLE,
                PhonebookDataSource.CELL_PHONE_NUMBER, PhonebookDataSource.IM_ID, PhonebookDataSource.JOB_DEPT,
                PhonebookDataSource.FAX_NUMBER, PhonebookDataSource.ALTERNATE_IM_ID,
                PhonebookDataSource.COMPANY_NAME };

        private static final String[] FIELDS_HOME = { PhonebookDataSource.HOME_STREET,
                PhonebookDataSource.HOME_STATE, PhonebookDataSource.HOME_ZIP, PhonebookDataSource.HOME_CITY,
                PhonebookDataSource.HOME_COUNTRY, PhonebookDataSource.HOME_PHONE_NUMBER };

        private static final String[] FIELDS_OFFICE = { PhonebookDataSource.OFFICE_DESIGNATION,
                PhonebookDataSource.OFFICE_STREET, PhonebookDataSource.OFFICE_COUNTRY,
                PhonebookDataSource.ASSISTANT_NAME, PhonebookDataSource.OFFICE_CITY, PhonebookDataSource.OFFICE_ZIP,
                PhonebookDataSource.ASSISTANT_PHONE_NUMBER, PhonebookDataSource.OFFICE_STATE };

        private static PhonebookGrid s_phonebookGrid;

        private final ContactInformationForm m_generalForm;
        private final ContactInformationForm m_homeForm;
        private final ContactInformationForm m_officeForm;
        private final AvatarSection m_avatar = new AvatarSection();
        private final ValuesManager m_vm;

        private boolean m_edit;

        public Details(final PhonebookGrid grid) {
            this(grid, 4, 2, 2);
        }

        public Details(final PhonebookGrid grid, int generalCols, int homeCols, int officeCols) {
            m_generalForm = new ContactInformationForm(FIELDS_GENERAL, "generalForm", generalCols,
                    s_searchConstants);
            m_homeForm = new ContactInformationForm(FIELDS_HOME, "homeForm", homeCols, s_searchConstants);
            m_officeForm = new ContactInformationForm(FIELDS_OFFICE, "officeForm", officeCols, s_searchConstants);

            m_generalForm.addRequiredValidator(PhonebookDataSource.FIRST_NAME);
            m_generalForm.addRequiredValidator(PhonebookDataSource.LAST_NAME);
            m_generalForm.addRequiredValidator(PhonebookDataSource.NUMBER);
            m_generalForm.addEmailValidator(PhonebookDataSource.EMAIL_ADDRESS);
            m_generalForm.addEmailValidator(PhonebookDataSource.ALTERNATE_EMAIL_ADDRESS);

            m_vm = new ValuesManager();

            m_generalForm.setValuesManager(m_vm);
            m_homeForm.setValuesManager(m_vm);
            m_officeForm.setValuesManager(m_vm);

            s_phonebookGrid = grid;
        }

        public void onSaveClick(String entryId) {
            if (m_edit && (entryId.isEmpty() || entryId.equals(DUMMY_ID))) {
                SC.say(s_searchConstants.editRecordWarning());
            } else {
                if (m_vm.validate() && m_vm.valuesHaveChanged()) {
                    if (m_edit) {
                        ((PhonebookDataSource) s_phonebookGrid.getDataSource()).editEntry(entryId, m_vm,
                                s_searchConstants.editContactSuccessMessage());
                        s_phonebookGrid.refreshRecordsWithDelay();
                    } else {
                        ((PhonebookDataSource) s_phonebookGrid.getDataSource()).addEntry(entryId, m_vm,
                                s_searchConstants.addContactSuccessMessage());
                        s_phonebookGrid.refreshRecordsWithDelay();
                        addData();
                    }
                }
            }
        }

        public void onResetClick() {
            m_vm.resetValues();
        }

        public void onCancelClick() {
            m_vm.clearErrors(true);
            setData(s_phonebookGrid.getSelectedRecord());
        }

        public void setData(ListGridRecord selection) {
            m_avatar.update(selection);
            m_generalForm.setData(selection);
            m_homeForm.setData(selection);
            m_officeForm.setData(selection);
        }

        public void editData() {
            m_vm.rememberValues();
            m_edit = true;

            m_generalForm.editData();
            m_homeForm.editData();
            m_officeForm.editData();
        }

        public void addData() {
            m_vm.clearValues();
            m_edit = false;

            m_generalForm.addData();
            m_homeForm.addData();
            m_officeForm.addData();
        }

        public AvatarSection getAvatarSection() {
            return m_avatar;
        }

        public ContactInformationForm getGeneralForm() {
            return m_generalForm;
        }

        public ContactInformationForm getHomeForm() {
            return m_homeForm;
        }

        public ContactInformationForm getOfficeForm() {
            return m_officeForm;
        }
    }

    private static class ModalWindow extends Window {

        private void createWindow(String title, String help, DynamicForm form, IButton submitButton) {
            setAutoSize(true);

            setTitle(title);
            setShowMinimizeButton(false);
            setIsModal(true);
            setAutoCenter(true);
            addCloseClickHandler(new CloseClickHandler() {
                public void onCloseClick(CloseClientEvent event) {
                    destroy();
                }
            });

            Label helpText = new Label();
            helpText.setContents(help);
            helpText.setWrap(false);
            helpText.setStyleName("formHint");
            helpText.setAutoFit(true);

            VLayout importLayout = new VLayout();
            importLayout.addMember(helpText);
            importLayout.addMember(form);
            importLayout.addMember(submitButton);
            importLayout.setAutoHeight();
            importLayout.setAutoWidth();
            importLayout.setLayoutAlign(Alignment.CENTER);
            addItem(importLayout);

        }

    }

    private static class ClickToCallModalWindow extends ModalWindow {

        public ClickToCallModalWindow(LinkedHashMap<String, String> phoneMap) {

            final TextItem numberToDail = new TextItem();
            numberToDail.setShowTitle(false);
            if (0 < phoneMap.keySet().size()) {
                numberToDail.setValue(formatPhoneNumber((String) phoneMap.keySet().toArray()[0]));
            }

            final SelectItem selectNumber = new SelectItem();
            selectNumber.setValueMap(phoneMap);
            selectNumber.addChangedHandler(new ChangedHandler() {
                @Override
                public void onChanged(ChangedEvent event) {
                    String number = (String) event.getValue();
                    numberToDail.setValue(formatPhoneNumber(number));
                }
            });
            selectNumber.setShowTitle(false);
            selectNumber.setDefaultToFirstOption(true);

            final DynamicForm form = new DynamicForm();
            form.setFields(new FormItem[] { numberToDail, selectNumber });
            form.setLayoutAlign(Alignment.CENTER);

            IButton callButton = new IButton();
            callButton.setTitle(s_searchConstants.call());
            callButton.setLayoutAlign(Alignment.CENTER);
            callButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    String dialNumber = (String) numberToDail.getValue();
                    clickToCallRestCall(dialNumber);
                    destroy();
                }
            });

            super.createWindow(s_searchConstants.clickToCall(), s_searchConstants.clickToCallHelp(), form,
                    callButton);

        }
    }

    private static void clickToCallRestCall(String number) {
        Map<Integer, String> errorStatuses = new HashMap<Integer, String>();
        errorStatuses.put(CLICK_TO_CALL_ERROR, s_searchConstants.invalidPhoneNumber());
        HttpRequestBuilder.doPut("/sipxconfig/rest/my/call/" + URL.encodeComponent(number), null, CONTENT_TYPE,
                VARIANT, null, errorStatuses);
    }

    /**
     * This method strips the phone number of spaces.
     *
     * @param phoneNumber the number to format
     * @return the formatted number
     */
    private static String formatPhoneNumber(String phoneNumber) {
        return phoneNumber.replaceAll("\\s", EMPTY_STRING);
    }

    private static void updateShowOnPhoneStatus(Boolean show, String successMessage) {
        HttpRequestBuilder.doPut("/sipxconfig/rest/my/phonebook/showContactsOnPhone/" + show, successMessage);
    }

    private static class AvatarSection extends VLayout {
        private static final String DEFAULT_AVATAR_URL = "https://secure.gravatar.com/avatar";

        private final Img m_avatar;
        private final Label m_contactName;
        private final ClickToCallButton m_clickToCall;

        public AvatarSection() {
            m_avatar = new Img();
            m_avatar.setStyleName("gwtAvatarImg");
            m_avatar.setWidth(140);
            m_avatar.setHeight(140);
            m_avatar.setLayoutAlign(Alignment.CENTER);

            m_contactName = new Label();
            m_contactName.setStyleName("gwtContactNameStyle");
            m_contactName.setAlign(Alignment.CENTER);
            m_contactName.setAutoHeight();
            m_contactName.setCanSelectText(true);
            m_contactName.setCursor(Cursor.TEXT);

            final HLayout clickToCallButtonLayout = new HLayout();
            m_clickToCall = new ClickToCallButton();
            clickToCallButtonLayout.addMember(m_clickToCall);
            clickToCallButtonLayout.setPadding(15);
            clickToCallButtonLayout.setWidth100();
            clickToCallButtonLayout.setAlign(Alignment.CENTER);

            addMember(m_avatar);
            addMember(m_contactName);
            addMember(clickToCallButtonLayout);

            setWidth(150);
            setHeight100();
        }

        public void update(ListGridRecord record) {
            String avatar = getNullSafeValue(record.getAttributeAsString(PhonebookDataSource.AVATAR));
            if (avatar.isEmpty()) {
                avatar = DEFAULT_AVATAR_URL;
            }

            String firstName = getNullSafeValue(record.getAttributeAsString(PhonebookDataSource.FIRST_NAME));
            String lastName = getNullSafeValue(record.getAttributeAsString(PhonebookDataSource.LAST_NAME));
            m_clickToCall.update(record);
            refreshSection(avatar, firstName + " " + lastName);
        }

        private void refreshSection(String imgSrc, String contactName) {
            m_avatar.setSrc(imgSrc);
            m_contactName.setContents(StringUtil.asHTML(contactName));
        }

        private String getNullSafeValue(String value) {
            if (value == null || value.equalsIgnoreCase("null")) {
                return EMPTY_STRING;
            }
            return value;
        }
    }

    private static class ClickToCallButton extends IButton {
        private final LinkedHashMap<String, String> m_phoneMap;

        public ClickToCallButton() {
            super(s_searchConstants.call());
            m_phoneMap = new LinkedHashMap<String, String>();

            addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    if (!m_phoneMap.isEmpty()) {
                        final ClickToCallModalWindow winModal = new ClickToCallModalWindow(m_phoneMap);
                        winModal.show();
                    }
                }
            });
        }

        public void update(ListGridRecord listGridRecord) {
            m_phoneMap.clear();

            if (null == listGridRecord) {
                return;
            }

            for (String fieldName : FIELDS_PHONENUMBERS) {
                String numberToDial = listGridRecord.getAttributeAsString(fieldName);
                if (numberToDial != null && !EMPTY_STRING.equals(numberToDial)) {
                    m_phoneMap.put(numberToDial, s_searchConstants.getString(fieldName));
                }
            }

            if (m_phoneMap.isEmpty()) {
                m_phoneMap.put(EMPTY_STRING, s_searchConstants.emptyPhoneList());
            }
        }
    }

    private static class GmailImportModalWindow extends ModalWindow {

        private static final String GMAIL_FIELD_NAME = "mail";
        private static final String PASSWORD_FIELD_NAME = "pwd";
        private static final int WRONG_GMAIL_CREDENTIALS_CODE = 743;
        private static final int INTERNAL_GMAIL_ERROR_CODE = 744;
        private static final int GMAIL_CONNECTION_FAILED_CODE = 745;

        public GmailImportModalWindow(final ListGrid phonebookGrid) {

            final DynamicForm gmailForm = new DynamicForm();
            gmailForm.setNumCols(4);

            TextItem emailItem = new TextItem(GMAIL_FIELD_NAME, s_searchConstants.account());
            PasswordItem passwordItem = new PasswordItem(PASSWORD_FIELD_NAME, s_searchConstants.password());
            gmailForm.setFields(emailItem, passwordItem);

            IButton importFromGmail = new IButton(s_searchConstants.importLabel());
            importFromGmail.setLayoutAlign(Alignment.CENTER);
            importFromGmail.setAutoFit(true);
            importFromGmail.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    String email = gmailForm.getValueAsString(GMAIL_FIELD_NAME);
                    String pass = gmailForm.getValueAsString(PASSWORD_FIELD_NAME);
                    String url = "/sipxconfig/rest/my/phonebook/googleImport";
                    RequestBuilder reqBuilder = new RequestBuilder(RequestBuilder.POST, url);
                    reqBuilder.setHeader(CONTENT_TYPE, VARIANT);
                    try {
                        reqBuilder.sendRequest(createPostData(email, pass), new RequestCallback() {

                            @Override
                            public void onResponseReceived(Request request, Response response) {
                                int httpStatusCode = response.getStatusCode();
                                String message = s_searchConstants.gmailImportFailed();
                                if (httpStatusCode == WRONG_GMAIL_CREDENTIALS_CODE) {
                                    message = s_searchConstants.wrongGmailCredentials();
                                } else if (httpStatusCode == INTERNAL_GMAIL_ERROR_CODE) {
                                    message = s_searchConstants.internalGmailProblems();
                                } else if (httpStatusCode == GMAIL_CONNECTION_FAILED_CODE) {
                                    message = s_searchConstants.gmailConnectionFailed();
                                } else if (httpStatusCode == Response.SC_OK) {
                                    message = s_searchConstants.gmailImportSuccess();
                                }
                                SC.say(message);
                                phonebookGrid.setRecords(new ListGridRecord[0]);
                                phonebookGrid.filterData();
                            }

                            @Override
                            public void onError(Request request, Throwable exception) {
                                SC.warn(s_searchConstants.gmailImportFailed());
                            }
                        });
                    } catch (RequestException ex) {
                        SC.warn(s_searchConstants.gmailImportFailed());
                    }
                    destroy();
                }
            });

            super.createWindow(s_searchConstants.gmailImportTitle(), s_searchConstants.gmailImportHelp(), gmailForm,
                    importFromGmail);
        }

        private String createPostData(String account, String password) {
            JSONObject values = new JSONObject();
            values.put("account", new JSONString(account));
            values.put("password", new JSONString(password));
            JSONObject credentials = new JSONObject();
            credentials.put("credentials", values);
            return credentials.toString();
        }
    }
}