/*
* Copyright (C) 2014 Jan Pokorsky
*
* This program 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.cas.lib.proarc.webapp.client.action;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.smartgwt.client.data.DSCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.i18n.SmartGwtMessages;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.PromptStyle;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.TextAreaWrap;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.CheckboxItem;
import com.smartgwt.client.widgets.form.fields.TextAreaItem;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.events.SelectionUpdatedEvent;
import com.smartgwt.client.widgets.grid.events.SelectionUpdatedHandler;
import com.smartgwt.client.widgets.layout.HStack;
import com.smartgwt.client.widgets.layout.VLayout;
import cz.cas.lib.proarc.webapp.client.ClientMessages;
import cz.cas.lib.proarc.webapp.client.ClientUtils;
import cz.cas.lib.proarc.webapp.client.ds.DigitalObjectDataSource.DigitalObject;
import cz.cas.lib.proarc.webapp.client.ds.ExportDataSource;
import cz.cas.lib.proarc.webapp.client.ds.RestConfig;
import cz.cas.lib.proarc.webapp.shared.rest.ExportResourceApi;
import java.util.Arrays;
import java.util.HashSet;
/**
* The DESA export action.
*
* @author Jan Pokorsky
*/
public class DesaExportAction extends AbstractAction {
private static final HashSet<String> MODELS = new HashSet<String>(Arrays.asList(
"model:desFolder", "model:desInternalRecord", "model:desExternalRecord",
"model:derFolder", "model:derDocument"));
private final ClientMessages i18n;
private final ExportType type;
private enum ExportType { DOWNLOAD, EXPORT, VALIDATION}
/**
* The action to export or validate digital objects.
*/
public static DesaExportAction export(ClientMessages i18n) {
return new DesaExportAction(i18n, i18n.DesaExportAction_Title(),
i18n.DesaExportAction_Hint(), ExportType.VALIDATION);
}
/**
* The action to export and download a digital object to client.
*/
public static DesaExportAction download(ClientMessages i18n) {
return new DesaExportAction(i18n, i18n.DesaDownloadExportAction_Title(),
i18n.DesaDownloadExportAction_Hint(), ExportType.DOWNLOAD);
}
DesaExportAction(ClientMessages i18n, String title, String hint, ExportType type) {
super(title, null, hint);
this.i18n = i18n;
this.type = type;
}
@Override
public boolean accept(ActionEvent event) {
Object[] selection = Actions.getSelection(event);
boolean accept = false;
if (selection != null && selection instanceof Record[]) {
Record[] records = (Record[]) selection;
if (ExportType.DOWNLOAD == type) {
accept = records.length == 1 && acceptDesa(records);
} else if (records.length > 0 && acceptDesa(records)) {
accept = true;
}
}
return accept;
}
private boolean acceptDesa(Record[] records) {
boolean accept = false;
for (Record record : records) {
DigitalObject dobj = DigitalObject.createOrNull(record);
if (dobj != null) {
// MetaModelRecord model = dobj.getModel();
// String metadataFormat = model.getMetadataFormat();
String modelId = dobj.getModelId();
// XXX hack; it needs support to query model/object for action availability
if (MODELS.contains(modelId)) {
accept = true;
continue;
}
}
accept = false;
break;
}
return accept;
}
@Override
public void performAction(ActionEvent event) {
Record[] records = Actions.getSelection(event);
String[] pids = ClientUtils.toFieldValues(records, ExportResourceApi.DESA_PID_PARAM);
switch (type) {
case DOWNLOAD:
download(pids);
break;
default:
askForExportOptions(pids);
}
}
private void download(final String[] pids) {
Record export = new Record();
export.setAttribute(ExportResourceApi.DESA_PID_PARAM, pids[0]);
export.setAttribute(ExportResourceApi.DESA_FORDOWNLOAD_PARAM, true);
DSRequest dsRequest = new DSRequest();
dsRequest.setPromptStyle(PromptStyle.DIALOG);
dsRequest.setPrompt(i18n.KrameriusExportAction_Add_Msg());
DataSource ds = ExportDataSource.getDesa();
ds.addData(export, new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
if (RestConfig.isStatusOk(response)) {
Record[] data = response.getData();
Record[] errors = data[0].getAttributeAsRecordArray(ExportResourceApi.RESULT_ERRORS);
if (errors != null && errors.length > 0) {
ExportResultWidget.showErrors(errors);
} else {
String token = data[0].getAttribute(ExportResourceApi.RESULT_TOKEN);
openResult(pids[0], token);
}
}
}
}, dsRequest);
}
private void askForExportOptions(String[] pids) {
if (pids == null || pids.length == 0) {
return ;
}
Record export = new Record();
export.setAttribute(ExportResourceApi.DESA_PID_PARAM, pids);
ExportOptionsWidget.showOptions(export, new Callback<Record, Void>() {
@Override
public void onFailure(Void reason) {
// no-op
}
@Override
public void onSuccess(Record result) {
exportOrValidate(result);
}
});
}
private void exportOrValidate(final Record export) {
DSRequest dsRequest = new DSRequest();
dsRequest.setPromptStyle(PromptStyle.DIALOG);
dsRequest.setPrompt(i18n.KrameriusExportAction_Add_Msg());
DataSource ds = ExportDataSource.getDesa();
ds.addData(export, new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
if (RestConfig.isStatusOk(response)) {
Record[] data = response.getData();
RecordList erl = errorsFromExportResult(data);
if (erl.isEmpty()) {
String dryRun = export.getAttribute(ExportResourceApi.DESA_DRYRUN_PARAM);
SC.say(dryRun == null
? i18n.DesaExportAction_ExportDone_Msg()
: i18n.DesaExportAction_ValidationDone_Msg());
} else {
ExportResultWidget.showErrors(erl.toArray());
}
}
}
}, dsRequest);
}
private RecordList errorsFromExportResult(Record[] exportResults) {
RecordList recordList = new RecordList();
for (Record result : exportResults) {
Record[] errors = result.getAttributeAsRecordArray(ExportResourceApi.RESULT_ERRORS);
if (errors != null && errors.length > 0) {
recordList.addList(errors);
}
}
return recordList;
}
private void openResult(String pid, String token) {
if (pid == null) {
throw new IllegalArgumentException("pid");
}
StringBuilder sb = new StringBuilder();
sb.append(RestConfig.URL_EXPORT_DESA);
sb.append('?').append(ExportResourceApi.DESA_PID_PARAM).append('=').append(pid);
sb.append('&').append(ExportResourceApi.RESULT_TOKEN).append('=').append(token);
com.google.gwt.user.client.Window.open(sb.toString(), "_blanc", "");
}
// ask to send to DESA already exported?
// ask to keep folder with export, ask for folder name?
/**
* Displays the form to collect options for a new export.
*/
static final class ExportOptionsWidget {
private static ExportOptionsWidget INSTANCE;
private final ClientMessages i18n;
private final DynamicForm form;
private final Label label;
private Window window;
private Callback<Record, Void> callback;
public static void showOptions(Record init, Callback<Record, Void> callback) {
if (INSTANCE == null) {
INSTANCE = new ExportOptionsWidget();
}
INSTANCE.showWindow(init, callback);
}
public ExportOptionsWidget() {
i18n = GWT.create(ClientMessages.class);
label = new Label();
label.setMargin(5);
label.setWidth100();
label.setAutoHeight();
label.setWrap(false);
form = new DynamicForm();
CheckboxItem hierarchy = new CheckboxItem(ExportResourceApi.DESA_HIERARCHY_PARAM,
i18n.ExportOptionsWidget_Hierarchy_Title());
hierarchy.setPrompt(i18n.ExportOptionsWidget_Hierarchy_Hint());
form.setFields(hierarchy);
}
private void showWindow(Record init, Callback<Record, Void> callback) {
if (window == null) {
VLayout container = new VLayout();
container.setMembers(label, form, createButtons());
container.setMargin(5);
window = new Window();
window.setAutoCenter(true);
window.setAutoSize(true);
window.setIsModal(true);
window.addItem(container);
window.setTitle(i18n.ExportOptionsWidget_Window_Title());
window.setShowMinimizeButton(false);
window.setKeepInParentRect(true);
window.setShowModalMask(true);
window.addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClickEvent event) {
finish().onFailure(null);
}
});
}
window.show();
form.clearValues();
form.focus();
String[] pids = init.getAttributeAsStringArray(ExportResourceApi.DESA_PID_PARAM);
label.setContents(i18n.ExportOptionsWidget_Selection_Title(String.valueOf(pids.length)));
form.editRecord(init);
this.callback = callback != null ? callback : ClientUtils.<Record, Void>emptyCallback();
}
private Callback<Record, Void> finish() {
window.hide();
Callback<Record, Void> c = callback;
callback = ClientUtils.emptyCallback();
return c;
}
private Canvas createButtons() {
SmartGwtMessages i18nSgwt = ClientUtils.createSmartGwtMessages();
IButton btnExport = new IButton(i18n.ExportOptionsWidget_ExportBtn_Title(), new com.smartgwt.client.widgets.events.ClickHandler() {
@Override
public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) {
Record r = new Record(form.getValues());
finish().onSuccess(r);
}
});
btnExport.setPrompt(i18n.ExportOptionsWidget_ExportBtn_Hint());
IButton btnValidate = new IButton(i18n.ExportOptionsWidget_ValidateBtn_Title(), new com.smartgwt.client.widgets.events.ClickHandler() {
@Override
public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) {
Record r = new Record(form.getValues());
r.setAttribute(ExportResourceApi.DESA_DRYRUN_PARAM, true);
finish().onSuccess(r);
}
});
btnValidate.setPrompt(i18n.ExportOptionsWidget_ValidateBtn_Hint());
IButton btnCancel = new IButton(i18nSgwt.dialog_CancelButtonTitle(), new com.smartgwt.client.widgets.events.ClickHandler() {
@Override
public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) {
finish().onFailure(null);
}
});
HStack btnLayout = new HStack(5);
btnLayout.setAutoHeight();
btnLayout.setLayoutTopMargin(20);
btnLayout.setLayoutAlign(Alignment.CENTER);
btnLayout.setMembers(btnValidate, btnExport, btnCancel);
return btnLayout;
}
}
/**
* Presents errors of the digital objects export result. Use {@link #showErrors} for
* shared instance.
* @see cz.cas.lib.proarc.webapp.server.rest.ExportResource.ExportError
*/
static final class ExportResultWidget {
private final ClientMessages i18n;
private static ExportResultWidget INSTANCE;
private final ListGrid grid;
private final Canvas widget;
private final DynamicForm logForm;
private Window window;
/**
* Shows errors in the shared window instance.
* @param result array of errors
*/
public static void showErrors(Record[] result) {
if (INSTANCE == null) {
INSTANCE = new ExportResultWidget();
}
INSTANCE.getGrid().setData(result);
INSTANCE.getGrid().selectSingleRecord(result[0]);
INSTANCE.showWindow();
}
public ExportResultWidget() {
i18n = GWT.create(ClientMessages.class);
VLayout vLayout = new VLayout();
vLayout.setWidth100();
vLayout.setHeight100();
grid = new ListGrid();
grid.setSelectionType(SelectionStyle.SINGLE);
grid.setFixedRecordHeights(false);
grid.setWrapCells(true);
ListGridField pidField = new ListGridField(ExportResourceApi.RESULT_ERROR_PID,
i18n.ExportResultWidget_PID_Title());
ListGridField errorField = new ListGridField(ExportResourceApi.RESULT_ERROR_MESSAGE,
i18n.ExportResultWidget_Message_Title());
grid.setFields(pidField, errorField);
grid.addSelectionUpdatedHandler(new SelectionUpdatedHandler() {
@Override
public void onSelectionUpdated(SelectionUpdatedEvent event) {
logForm.editSelectedData(grid);
}
});
logForm = createLogForm();
vLayout.setMembers(grid, logForm);
this.widget = vLayout;
}
public ListGrid getGrid() {
return grid;
}
public Canvas getWidget() {
return widget;
}
private void showWindow() {
if (window == null) {
window = new Window();
window.setWidth(600);
window.setHeight(400);
window.setAutoCenter(true);
window.setIsModal(true);
window.addItem(widget);
window.setTitle(i18n.ExportResultWidget_Window_Title());
window.setShowMinimizeButton(false);
window.setShowMaximizeButton(true);
window.setKeepInParentRect(true);
window.setShowModalMask(true);
window.setCanDragResize(true);
}
window.show();
grid.focus();
}
private DynamicForm createLogForm() {
DynamicForm form = new DynamicForm();
form.setBrowserSpellCheck(false);
form.setCanEdit(false);
form.setWidth100();
form.setHeight("40%");
TextAreaItem textAreaItem = new TextAreaItem(ExportResourceApi.RESULT_ERROR_LOG);
textAreaItem.setColSpan("*");
textAreaItem.setHeight("*");
textAreaItem.setWrap(TextAreaWrap.OFF);
textAreaItem.setShowTitle(false);
textAreaItem.setWidth("*");
textAreaItem.setCanEdit(false);
form.setItems(textAreaItem);
return form;
}
}
}