/* * Copyright (C) 2011 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.ds; import com.google.gwt.core.client.Callback; import com.google.gwt.core.shared.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.DataSourceField; import com.smartgwt.client.data.Record; import com.smartgwt.client.data.RestDataSource; import com.smartgwt.client.data.fields.DataSourceImageField; import com.smartgwt.client.data.fields.DataSourceIntegerField; import com.smartgwt.client.data.fields.DataSourceTextField; import com.smartgwt.client.types.DSDataFormat; import com.smartgwt.client.types.FieldType; import com.smartgwt.client.types.PromptStyle; import cz.cas.lib.proarc.webapp.client.ClientMessages; import cz.cas.lib.proarc.webapp.client.ClientUtils; import cz.cas.lib.proarc.webapp.client.widget.StatusView; import cz.cas.lib.proarc.webapp.shared.rest.ImportResourceApi; import java.util.ArrayList; import java.util.Arrays; /** * * @author Jan Pokorsky */ public final class ImportBatchItemDataSource extends RestDataSource { public static final String ID = "ImportBatchItemDataSource"; public static final String FIELD_BATCHID = ImportResourceApi.BATCHITEM_BATCHID; public static final String FIELD_FILENAME = ImportResourceApi.BATCHITEM_FILENAME; public static final String FIELD_PID = ImportResourceApi.BATCHITEM_PID; public static final String FIELD_MODEL = ImportResourceApi.BATCHITEM_MODEL; public static final String FIELD_PAGE_TYPE = ImportResourceApi.BATCHITEM_PAGETYPE; public static final String FIELD_PAGE_TYPE_LABEL = ImportResourceApi.BATCHITEM_PAGETYPELABEL; public static final String FIELD_PAGE_INDEX = ImportResourceApi.BATCHITEM_PAGEINDEX; public static final String FIELD_PAGE_NUMBER = ImportResourceApi.BATCHITEM_PAGENUMBER; public static final String FIELD_TIMESTAMP = ImportResourceApi.BATCHITEM_TIMESTAMP; public static final String FIELD_USER = ImportResourceApi.BATCHITEM_USER; /** synthetic field holding batchid and pid URL parameters */ public static final String FIELD_PREVIEW = "preview"; /** synthetic field holding batchid and pid URL parameters */ public static final String FIELD_THUMBNAIL = "thumbnail"; public ImportBatchItemDataSource() { setID(ID); setDataFormat(DSDataFormat.JSON); setDataURL(RestConfig.URL_IMPORT_BATCH_ITEM); DataSourceField pid = new DataSourceField(FIELD_PID, FieldType.TEXT); pid.setPrimaryKey(true); DataSourceIntegerField batchId = new DataSourceIntegerField(FIELD_BATCHID); batchId.setPrimaryKey(true); batchId.setForeignKey(ImportBatchDataSource.ID + '.' + ImportBatchDataSource.FIELD_ID); DataSourceField timestamp = new DataSourceField(FIELD_TIMESTAMP, FieldType.TEXT); timestamp.setRequired(true); timestamp.setHidden(true); DataSourceTextField filename = new DataSourceTextField(FIELD_FILENAME); DataSourceTextField user = new DataSourceTextField(FIELD_USER); DataSourceTextField model = new DataSourceTextField(FIELD_MODEL); model.setForeignKey(MetaModelDataSource.ID + '.' + MetaModelDataSource.FIELD_PID); DataSourceImageField preview = new DataSourceImageField(FIELD_PREVIEW); preview.setImageURLPrefix(RestConfig.URL_DIGOBJECT_PREVIEW + "?"); DataSourceImageField thumbnail = new DataSourceImageField(FIELD_THUMBNAIL); thumbnail.setImageURLPrefix(RestConfig.URL_DIGOBJECT_THUMBNAIL + "?"); DataSourceField pageType = new DataSourceField(FIELD_PAGE_TYPE, FieldType.TEXT); DataSourceField pageTypeLabel = new DataSourceField(FIELD_PAGE_TYPE_LABEL, FieldType.TEXT); DataSourceField pageIndex = new DataSourceField(FIELD_PAGE_INDEX, FieldType.INTEGER, "Page Index"); DataSourceField pageNumber = new DataSourceField(FIELD_PAGE_NUMBER, FieldType.TEXT, "Page Number"); setFields(pid, batchId, timestamp, filename, user, model, preview, thumbnail, pageIndex, pageNumber, pageType, pageTypeLabel); setOperationBindings(RestConfig.createDeleteOperation()); setRequestProperties(RestConfig.createRestRequest(getDataFormat())); } @Override protected void transformResponse(DSResponse response, DSRequest request, Object data) { super.transformResponse(response, request, data); if (RestConfig.isStatusOk(response)) { for (Record record : response.getData()) { String pid = record.getAttribute(FIELD_PID); String batchId = record.getAttribute(FIELD_BATCHID); String imgParams = ClientUtils.format("%s=%s&%s=%s", FIELD_PID, pid, FIELD_BATCHID, batchId); record.setAttribute(FIELD_PREVIEW, imgParams); record.setAttribute(FIELD_THUMBNAIL, imgParams); } } else { // In case of any error DataSource invokes further fetches in never ending loop // Following seems to help. response.setEndRow(0); response.setTotalRows(0); } } /** * Removes list of digital objects from the given batch import. * @param callback callback to get the result * @param batchId import ID * @param pids digital object IDs */ public void delete(final Callback<Record[], String> callback, String batchId, String... pids) { DeleteTask task = new DeleteTask(this, batchId, pids, callback); task.delete(); } public static ImportBatchItemDataSource getInstance() { ImportBatchItemDataSource ds = (ImportBatchItemDataSource) DataSource.get(ID); ds = ds != null ? ds : new ImportBatchItemDataSource(); return ds; } /** * Deletes objects from a given batch import. * It splits PIDs array to chunks not to exceed URL length limit (8k). * <p>The alternative is to increase {@code Connector@maxHttpHeaderSize} * at {@code server.xml}. * <p>SmartGWT does not allow to use HTTP DELETE method with body. */ private static class DeleteTask { private static int CHUNK_LIMIT = 100; private int itemIndex = 0; private final DataSource ds; private final String batchId; private final String[] pids; private final Callback<Record[], String> callback; private final ClientMessages i18n; private final ArrayList<Record> deleted; public DeleteTask(DataSource ds, String batchId, String[] pids, Callback<Record[], String> callback) { this.ds = ds; this.pids = pids; this.callback = callback; this.i18n = GWT.create(ClientMessages.class); this.batchId = batchId; this.deleted = new ArrayList<Record>(pids.length); } public void delete() { deleteItem(); } private void deleteItem() { String[] chunk = nextChunk(); if (chunk == null) { StatusView.getInstance().show(i18n.DeleteAction_Done_Msg()); callback.onSuccess(deleted.toArray(new Record[0])); return ; } Record query = new Record(); query.setAttribute(FIELD_BATCHID, batchId); query.setAttribute(FIELD_PID, chunk); DSRequest dsRequest = new DSRequest(); dsRequest.setPromptStyle(PromptStyle.DIALOG); dsRequest.setPrompt(i18n.DeleteAction_Deleting_Msg()); // TileGrid.removeSelectedData uses queuing support in case of multi-selection. // It will require extra support on server. For now remove data in separate requests. //thumbGrid.removeSelectedData(); ds.removeData(query, new DSCallback() { @Override public void execute(DSResponse response, Object rawData, DSRequest request) { if (RestConfig.isStatusOk(response)) { ds.updateCaches(response, request); deleted.addAll(Arrays.asList(response.getData())); deleteItem(); } else { if (deleted.isEmpty()) { callback.onFailure(null); } else { callback.onSuccess(deleted.toArray(new Record[0])); callback.onFailure(null); } } } }, dsRequest); } private String[] nextChunk() { String[] chunk = null; int remainingLength = pids.length - itemIndex; if (remainingLength > 0) { if (pids.length <= CHUNK_LIMIT) { chunk = pids; } else { int sliceLength = remainingLength > CHUNK_LIMIT ? CHUNK_LIMIT : remainingLength; chunk = new String[sliceLength]; System.arraycopy(pids, itemIndex, chunk, 0, sliceLength); } itemIndex += chunk.length; } return chunk; } } }