/*
Copyright 2009-2013 Josh Drummond
This file is part of WebPasswordSafe.
WebPasswordSafe is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
WebPasswordSafe 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with WebPasswordSafe; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.webpasswordsafe.client.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.webpasswordsafe.client.ClientSessionUtil;
import net.webpasswordsafe.client.WebPasswordSafe;
import net.webpasswordsafe.client.i18n.TextMessages;
import net.webpasswordsafe.client.remote.PasswordService;
import net.webpasswordsafe.client.remote.UserService;
import net.webpasswordsafe.common.model.AccessLevel;
import net.webpasswordsafe.common.model.Subject;
import net.webpasswordsafe.common.model.Template;
import net.webpasswordsafe.common.model.TemplateDetail;
import net.webpasswordsafe.common.util.Constants;
import net.webpasswordsafe.common.util.Utils;
import net.webpasswordsafe.common.util.Constants.Function;
import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.data.BaseModel;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MessageBoxEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.util.Format;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.Info;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.Window;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.extjs.gxt.ui.client.widget.form.LabelField;
import com.extjs.gxt.ui.client.widget.form.SimpleComboBox;
import com.extjs.gxt.ui.client.widget.form.SimpleComboValue;
import com.extjs.gxt.ui.client.widget.form.TextField;
import com.extjs.gxt.ui.client.widget.form.ComboBox.TriggerAction;
import com.extjs.gxt.ui.client.widget.grid.CellEditor;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.EditorGrid;
import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel;
import com.extjs.gxt.ui.client.widget.layout.AbsoluteData;
import com.extjs.gxt.ui.client.widget.layout.AbsoluteLayout;
import com.extjs.gxt.ui.client.widget.form.CheckBox;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* @author Josh Drummond
*
*/
public class TemplateDialog extends Window
{
private Template template;
private TextField<String> templateNameTextBox;
private ListStore<TemplateData> permissionStore;
private EditorGrid<TemplateData> permissionGrid;
private ComboBox<SubjectData> comboSubjects;
private TemplateData selectedPermission;
private CheckBox chkbxShared;
private ListStore<SubjectData> subjectStore;
private ClientSessionUtil clientSessionUtil = ClientSessionUtil.getInstance();
private final static TextMessages textMessages = GWT.create(TextMessages.class);
public TemplateDialog(Template template)
{
this.template = template;
setSize("380", "385");
this.setResizable(false);
this.setHeading(textMessages.template());
this.setModal(true);
this.setLayout(new AbsoluteLayout());
permissionStore = new ListStore<TemplateData>();
final SimpleComboBox<AccessLevel> accessLevelCombo = new SimpleComboBox<AccessLevel>();
accessLevelCombo.setForceSelection(true);
accessLevelCombo.setEditable(false);
accessLevelCombo.setTriggerAction(TriggerAction.ALL);
accessLevelCombo.add(Arrays.asList(AccessLevel.values()));
CellEditor accessLevelEditor = new CellEditor(accessLevelCombo)
{
@Override
public Object preProcessValue(Object v)
{
if (v instanceof AccessLevel)
{
return accessLevelCombo.findModel((AccessLevel) v);
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public Object postProcessValue(Object v)
{
return ((SimpleComboValue<AccessLevel>) v).get(Constants.VALUE);
}
};
subjectStore = new ListStore<SubjectData>();
LabelField lblfldTemplateName = new LabelField(textMessages.name_());
add(lblfldTemplateName, new AbsoluteData(6, 6));
templateNameTextBox = new TextField<String>();
add(templateNameTextBox, new AbsoluteData(87, 6));
templateNameTextBox.setSize("276px", "22px");
List<ColumnConfig> config = new ArrayList<ColumnConfig>(2);
ColumnConfig column = new ColumnConfig();
column.setId(Constants.SUBJECT);
column.setHeader(textMessages.userGroup());
column.setWidth(216);
config.add(column);
column = new ColumnConfig();
column.setId(Constants.ACCESSLEVEL);
column.setHeader(textMessages.accessLevel());
column.setWidth(113);
column.setEditor(accessLevelEditor);
config.add(column);
ColumnModel cm = new ColumnModel(config);
permissionGrid = new EditorGrid<TemplateData>(permissionStore, cm);
permissionGrid.setBorders(true);
permissionGrid.setStripeRows(true);
GridSelectionModel<TemplateData> gsm = permissionGrid.getSelectionModel();
gsm.setSelectionMode(SelectionMode.SINGLE);
permissionGrid.addListener(Events.CellClick, new Listener<GridEvent<TemplateData>>()
{
@Override
public void handleEvent(GridEvent<TemplateData> ge)
{
selectedPermission = ge.getModel();
}
});
permissionGrid.setSize(200, 200);
add(permissionGrid, new AbsoluteData(3, 33));
permissionGrid.setSize("360px", "221px");
Button removeButton = new Button(textMessages.removeSelected(),
new SelectionListener<ButtonEvent>()
{
@Override
public void componentSelected(ButtonEvent ce)
{
doRemove();
}
});
add(removeButton, new AbsoluteData(258, 260));
removeButton.setSize("105px", "22px");
Button addUserButton = new Button(textMessages.add(),
new SelectionListener<ButtonEvent>()
{
@Override
public void componentSelected(ButtonEvent ce)
{
doAdd();
}
});
add(addUserButton, new AbsoluteData(160, 260));
addUserButton.setSize("51px", "22px");
comboSubjects = new ComboBox<SubjectData>();
add(comboSubjects, new AbsoluteData(3, 260));
comboSubjects.setEmptyText(textMessages.selectUserGroup());
comboSubjects.setDisplayField(Constants.NAME);
comboSubjects.setStore(subjectStore);
comboSubjects.setTypeAhead(true);
comboSubjects.setTriggerAction(TriggerAction.ALL);
Button btnRemoveAll = new Button(textMessages.removeAll());
btnRemoveAll.addSelectionListener(new SelectionListener<ButtonEvent>()
{
@Override
public void componentSelected(ButtonEvent ce)
{
doRemoveAll();
}
});
add(btnRemoveAll, new AbsoluteData(258, 284));
btnRemoveAll.setSize("105px", "22px");
chkbxShared = new CheckBox();
add(chkbxShared, new AbsoluteData(6, 284));
chkbxShared.setBoxLabel(textMessages.shared_());
chkbxShared.setHideLabel(true);
if (template.getId() != 0)
{
if (!clientSessionUtil.isAuthorized(Function.BYPASS_TEMPLATE_SHARING) &&
(ClientSessionUtil.getInstance().getLoggedInUser().getId() != template.getUser().getId()))
{
chkbxShared.setReadOnly(true);
}
}
Button saveButton = new Button(textMessages.save(),
new SelectionListener<ButtonEvent>()
{
@Override
public void componentSelected(ButtonEvent ce)
{
doSave();
}
});
Button deleteButton = new Button(textMessages.delete(),
new SelectionListener<ButtonEvent>()
{
@Override
public void componentSelected(ButtonEvent ce)
{
doVerifyDelete();
}
});
Button cancelButton = new Button(textMessages.cancel(),
new SelectionListener<ButtonEvent>()
{
@Override
public void componentSelected(ButtonEvent ce)
{
doCancel();
}
});
setButtonAlign(HorizontalAlignment.CENTER);
addButton(saveButton);
if (template.getId() != 0)
{
if (clientSessionUtil.isAuthorized(Function.BYPASS_TEMPLATE_SHARING) ||
(ClientSessionUtil.getInstance().getLoggedInUser().getId() == template.getUser().getId()))
{
addButton(deleteButton);
}
}
addButton(cancelButton);
setFields();
}
@Override
public void show()
{
super.show();
setFocusWidget(templateNameTextBox);
}
private void doRemoveAll()
{
permissionStore.removeAll();
}
private void doRemove()
{
TemplateData data = selectedPermission;
if (null != data)
{
permissionStore.remove(data);
selectedPermission = null;
}
}
private void doAdd()
{
SubjectData data = comboSubjects.getValue();
if (null != data)
{
permissionStore.add(new TemplateData(new TemplateDetail((Subject)data.get(Constants.SUBJECT), AccessLevel.READ)));
}
}
private void setFields()
{
templateNameTextBox.setValue(template.getName());
chkbxShared.setValue(template.isShared());
updateTemplateDetails();
updateSubjects();
}
private void updateTemplateDetails()
{
permissionStore.removeAll();
for (TemplateDetail templateDetail : template.getTemplateDetails())
{
permissionStore.add(new TemplateData(templateDetail));
}
}
private void updateSubjects()
{
AsyncCallback<List<Subject>> callback = new AsyncCallback<List<Subject>>()
{
@Override
public void onFailure(Throwable caught)
{
WebPasswordSafe.handleServerFailure(caught);
}
@Override
public void onSuccess(List<Subject> result)
{
for (Subject subject : result)
{
subjectStore.add(new SubjectData(subject));
}
}
};
UserService.Util.getInstance().getSubjects(true, callback);
}
private void doCancel()
{
permissionStore.rejectChanges();
hide();
}
private boolean validateFields()
{
if ("".equals(Utils.safeString(templateNameTextBox.getValue())))
{
MessageBox.alert(textMessages.error(), textMessages.mustEnterName(), null);
return false;
}
if (Utils.safeString(templateNameTextBox.getValue()).length() > Template.LENGTH_NAME)
{
MessageBox.alert(textMessages.error(), textMessages.tooLongName(), null);
return false;
}
return true;
}
private void doVerifyDelete()
{
MessageBox.confirm(textMessages.confirmDelete(), textMessages.templateConfirmDelete(), new Listener<MessageBoxEvent>()
{
@Override
public void handleEvent(MessageBoxEvent be)
{
if (be.getButtonClicked().getItemId().equals(Dialog.YES))
{
doDelete();
}
}
});
}
private void doDelete()
{
AsyncCallback<Void> callback = new AsyncCallback<Void>()
{
@Override
public void onFailure(Throwable caught)
{
WebPasswordSafe.handleServerFailure(caught);
}
@Override
public void onSuccess(Void result)
{
Info.display(textMessages.status(), textMessages.templateDeleted());
hide();
}
};
PasswordService.Util.getInstance().deleteTemplate(template, callback);
}
private void doSave()
{
permissionStore.commitChanges();
if (validateFields())
{
template.setName(Utils.safeString(templateNameTextBox.getValue()));
template.setShared(chkbxShared.getValue());
template.clearDetails();
for (TemplateData data : permissionStore.getModels())
{
TemplateDetail templateDetail = (TemplateDetail)data.get(Constants.TEMPLATEDETAIL);
String newAccessLevel = ((AccessLevel)data.get(Constants.ACCESSLEVEL)).name();
if (!newAccessLevel.equals(templateDetail.getAccessLevel()))
{
// if user changed the access level value in the GUI, treat it like a new permission
templateDetail = new TemplateDetail(templateDetail.getSubject(), AccessLevel.valueOf(newAccessLevel));
}
template.addDetail(templateDetail);
}
final AsyncCallback<Boolean> callbackCheck = new AsyncCallback<Boolean>()
{
@Override
public void onFailure(Throwable caught)
{
WebPasswordSafe.handleServerFailure(caught);
}
@Override
public void onSuccess(Boolean result)
{
// true => template name already taken, else go ahead and save
if (result)
{
MessageBox.alert(textMessages.error(), textMessages.templateNameExists(), null);
}
else
{
AsyncCallback<Void> callback = new AsyncCallback<Void>()
{
@Override
public void onFailure(Throwable caught)
{
WebPasswordSafe.handleServerFailure(caught);
}
@Override
public void onSuccess(Void result)
{
Info.display(textMessages.status(), textMessages.templateSaved());
hide();
}
};
if (template.getId() == 0)
{
PasswordService.Util.getInstance().addTemplate(template, callback);
}
else
{
PasswordService.Util.getInstance().updateTemplate(template, callback);
}
}
}
};
PasswordService.Util.getInstance().isTemplateTaken(template.getName(), template.getId(), callbackCheck);
}
}
private class TemplateData extends BaseModel
{
private static final long serialVersionUID = 1L;
public TemplateData(TemplateDetail templateDetail)
{
set(Constants.ID, templateDetail.getId());
set(Constants.SUBJECT, Format.htmlEncode(templateDetail.getSubject().getName()));
set(Constants.ACCESSLEVEL, templateDetail.getAccessLevelObj());
set(Constants.TEMPLATEDETAIL, templateDetail);
}
}
private class SubjectData extends BaseModel
{
private static final long serialVersionUID = 1L;
public SubjectData(Subject subject)
{
set(Constants.ID, subject.getId());
set(Constants.NAME, Format.htmlEncode(subject.getName()));
set(Constants.SUBJECT, subject);
}
}
}