Support uploading directly to a new Google spreadsheet.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@2243 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2011-09-16 18:04:55 +00:00
parent b585140c34
commit 5c446d28d0
9 changed files with 608 additions and 59 deletions

View File

@ -47,6 +47,7 @@ function init() {
RS.registerCommand(module, "authorize", Packages.com.google.refine.extension.gdata.AuthorizeCommand());
RS.registerCommand(module, "authorize2", Packages.com.google.refine.extension.gdata.AuthorizeCommand2());
RS.registerCommand(module, "deauthorize", Packages.com.google.refine.extension.gdata.DeAuthorizeCommand());
RS.registerCommand(module, "upload", Packages.com.google.refine.extension.gdata.UploadCommand());
// Register importer and exporter
var IM = Packages.com.google.refine.importing.ImportingManager;
@ -80,6 +81,15 @@ function init() {
"styles/importing-controller.less"
]
);
// Script files to inject into /project page
ClientSideResourceManager.addPaths(
"project/scripts",
module,
[
"scripts/project/exporters.js"
]
);
}
/*

View File

@ -0,0 +1,82 @@
/*
Copyright 2011, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
CustomTabularExporterDialog.uploadTargets.push({
id: 'gdata/google-spreadsheet',
label: 'A new Google spreadsheet',
handler: function(options, exportAllRows, onDone) {
var doUpload = function() {
var name = window.prompt('Enter name of new spreadsheet', theProject.metadata.name);
if (name) {
var dismiss = DialogSystem.showBusy('Uploading...');
$.post(
"/command/gdata/upload",
{
"project" : theProject.id,
"engine" : exportAllRows ? '' : JSON.stringify(ui.browsingEngine.getJSON()),
"name" : name,
"format" : options.format,
"options" : JSON.stringify(options)
},
function(o) {
dismiss();
if (o.url) {
window.open(o.url, '_blank');
}
onDone();
},
"json"
);
}
};
var messageListener = function(evt) {
window.removeEventListener("message", messageListener, false);
if ($.cookie('authsub_token')) {
doUpload();
}
};
var authenticate = function() {
window.addEventListener("message", messageListener, false);
window.open(
"/command/gdata/authorize",
"google-refine-gdata-signin",
"resizable=1,width=600,height=450"
);
};
authenticate();
}
});

View File

@ -1,4 +1,31 @@
package com.google.refine.extension.gdata;
/*
* Copyright (c) 2011, Thomas F. Morris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Google nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/package com.google.refine.extension.gdata;
import java.io.IOException;
import java.net.URL;
@ -33,7 +60,7 @@ public class AuthorizeCommand extends Command {
String requestUrl = AuthSubUtil.getRequestUrl(
authorizedUrl.toExternalForm(), // execution continues at authorized on redirect
"https://spreadsheets.google.com/feeds https://www.google.com/fusiontables/api/query",
"https://docs.google.com/feeds https://spreadsheets.google.com/feeds https://www.google.com/fusiontables/api/query",
false,
true);
response.sendRedirect(requestUrl);

View File

@ -1,3 +1,31 @@
/*
* Copyright (c) 2011, Thomas F. Morris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Google nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.google.refine.extension.gdata;
import java.io.IOException;

View File

@ -1,3 +1,31 @@
/*
* Copyright (c) 2011, Thomas F. Morris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Google nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.google.refine.extension.gdata;
import java.io.IOException;

View File

@ -0,0 +1,270 @@
/*
* Copyright (c) 2010, Thomas F. Morris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Google nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.google.refine.extension.gdata;
import java.io.IOException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONObject;
import org.json.JSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.spreadsheet.CellQuery;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.Link;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.batch.BatchOperationType;
import com.google.gdata.data.batch.BatchStatus;
import com.google.gdata.data.batch.BatchUtils;
import com.google.gdata.data.docs.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.Cell;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.util.ServiceException;
import com.google.refine.ProjectManager;
import com.google.refine.browsing.Engine;
import com.google.refine.commands.Command;
import com.google.refine.commands.HttpUtilities;
import com.google.refine.commands.project.ExportRowsCommand;
import com.google.refine.exporters.CustomizableTabularExporterUtilities;
import com.google.refine.exporters.TabularSerializer;
import com.google.refine.model.Project;
public class UploadCommand extends Command {
static final Logger logger = LoggerFactory.getLogger("gdata_upload");
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String token = TokenCookie.getToken(request);
if (token == null) {
HttpUtilities.respond(response, "error", "Not authorized");
return;
}
ProjectManager.singleton.setBusy(true);
try {
Project project = getProject(request);
Engine engine = getEngine(request, project);
Properties params = ExportRowsCommand.getRequestParameters(request);
String name = params.getProperty("name");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/json");
Writer w = response.getWriter();
JSONWriter writer = new JSONWriter(w);
try {
writer.object();
String url = upload(project, engine, params, token, name);
if (url != null) {
writer.key("status"); writer.value("ok");
writer.key("url"); writer.value(url);
} else {
writer.key("status"); writer.value("error");
writer.key("message"); writer.value("No such format");
}
} catch (Exception e) {
e.printStackTrace();
writer.key("status"); writer.value("error");
writer.key("message"); writer.value(e.getMessage());
} finally {
writer.endObject();
w.flush();
w.close();
}
} catch (Exception e) {
throw new ServletException(e);
} finally {
ProjectManager.singleton.setBusy(false);
}
}
static private String upload(
Project project, Engine engine, Properties params,
String token, String name) throws Exception {
String format = params.getProperty("format");
if ("gdata/google-spreadsheet".equals(format)) {
return uploadSpreadsheet(project, engine, params, token, name);
}
return null;
}
static private String uploadSpreadsheet(
final Project project, final Engine engine, final Properties params,
String token, String name)
throws MalformedURLException, IOException, ServiceException {
DocsService docsService = GDataExtension.getDocsService(token);
final SpreadsheetService spreadsheetService = GDataExtension.getSpreadsheetService(token);
SpreadsheetEntry spreadsheetEntry = new SpreadsheetEntry();
spreadsheetEntry.setTitle(new PlainTextConstruct(name));
final SpreadsheetEntry spreadsheetEntry2 = docsService.insert(
new URL("https://docs.google.com/feeds/default/private/full/"), spreadsheetEntry);
int[] size = CustomizableTabularExporterUtilities.countColumnsRows(
project, engine, params);
URL worksheetFeedUrl = spreadsheetEntry2.getWorksheetFeedUrl();
WorksheetEntry worksheetEntry = new WorksheetEntry(size[1], size[0]);
worksheetEntry.setTitle(new PlainTextConstruct("Uploaded Data"));
final WorksheetEntry worksheetEntry2 =
spreadsheetService.insert(worksheetFeedUrl, worksheetEntry);
spreadsheetEntry2.getDefaultWorksheet().delete();
new Thread() {
@Override
public void run() {
spreadsheetService.setProtocolVersion(SpreadsheetService.Versions.V1);
try {
uploadToCellFeed(
project, engine, params,
spreadsheetService,
spreadsheetEntry2,
worksheetEntry2);
} catch (Exception e) {
logger.error("Error uploading data to Google Spreadsheets", e);
}
}
}.start();
return spreadsheetEntry2.getDocumentLink().getHref();
}
static private void uploadToCellFeed(
Project project,
Engine engine,
Properties params,
final SpreadsheetService service,
final SpreadsheetEntry spreadsheetEntry,
final WorksheetEntry worksheetEntry)
throws IOException, ServiceException {
final URL cellFeedUrl = worksheetEntry.getCellFeedUrl();
final CellEntry[][] cellEntries =
new CellEntry[worksheetEntry.getRowCount()][worksheetEntry.getColCount()];
{
CellQuery cellQuery = new CellQuery(cellFeedUrl);
cellQuery.setReturnEmpty(true);
CellFeed fetchingCellFeed = service.getFeed(cellQuery, CellFeed.class);
for (CellEntry cellEntry : fetchingCellFeed.getEntries()) {
Cell cell = cellEntry.getCell();
cellEntries[cell.getRow() - 1][cell.getCol() - 1] = cellEntry;
}
}
TabularSerializer serializer = new TabularSerializer() {
CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
CellFeed batchRequest = null;
int row = 0;
@Override
public void startFile(JSONObject options) {
}
@Override
public void endFile() {
if (batchRequest != null) {
sendBatch();
}
}
private void sendBatch() {
try {
Link batchLink = cellFeed.getLink(Link.Rel.FEED_BATCH, Link.Type.ATOM);
CellFeed batchResponse = service.batch(new URL(batchLink.getHref()), batchRequest);
for (CellEntry entry : batchResponse.getEntries()) {
String batchId = BatchUtils.getBatchId(entry);
if (!BatchUtils.isSuccess(entry)) {
BatchStatus status = BatchUtils.getBatchStatus(entry);
logger.warn(
String.format(
"Error: %s failed (%s) %s\n",
batchId, status.getReason(), status.getContent()));
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
batchRequest = null;
}
@Override
public void addRow(List<CellData> cells, boolean isHeader) {
if (batchRequest == null) {
batchRequest = new CellFeed();
}
for (int c = 0; c < cells.size(); c++) {
CellData cellData = cells.get(c);
if (cellData != null && cellData.text != null) {
String cellId = String.format("R%sC%s", row + 1, c + 1);
CellEntry cellEntry = cellEntries[row][c];
cellEntry.changeInputValueLocal(cellData.text);
if (cellData.link != null) {
cellEntry.addHtmlLink(cellData.link, null, cellData.text);
}
cellEntry.setId(cellId);
BatchUtils.setBatchId(cellEntry, cellId);
BatchUtils.setBatchOperationType(cellEntry, BatchOperationType.UPDATE);
batchRequest.getEntries().add(cellEntry);
}
}
row++;
if (row % 20 == 0) {
sendBatch();
}
}
};
CustomizableTabularExporterUtilities.exportRows(
project, engine, params, serializer);
}
}

View File

@ -171,6 +171,34 @@ abstract public class CustomizableTabularExporterUtilities {
filteredRows.accept(project, visitor);
}
static public int[] countColumnsRows(
final Project project,
final Engine engine,
Properties params) {
RowCountingTabularSerializer serializer = new RowCountingTabularSerializer();
exportRows(project, engine, params, serializer);
return new int[] { serializer.columns, serializer.rows };
}
static private class RowCountingTabularSerializer implements TabularSerializer {
int columns;
int rows;
@Override
public void startFile(JSONObject options) {
}
@Override
public void endFile() {
}
@Override
public void addRow(List<CellData> cells, boolean isHeader) {
columns = Math.max(columns, cells.size());
rows++;
}
}
private enum ReconOutputMode {
ENTITY_NAME,
ENTITY_ID,

View File

@ -3,49 +3,12 @@
<div class="dialog-body" bind="dialogBody">
<div id="custom-tabular-exporter-tabs" class="refine-tabs">
<ul>
<li><a href="#custom-tabular-exporter-tabs-format">File Format</a></li>
<li><a href="#custom-tabular-exporter-tabs-content">Content</a></li>
<li><a href="#custom-tabular-exporter-tabs-download">Download</a></li>
<li bind="uploadTabHeader"><a href="#custom-tabular-exporter-tabs-upload">Upload</a></li>
<li><a href="#custom-tabular-exporter-tabs-code">Option Code</a></li>
</ul>
<div id="custom-tabular-exporter-tabs-format" style="display: none;"><div class="grid-layout grid-layout-for-ui layout-loose layout-full"><table>
<tr>
<th>Line-based text formats</th>
<th>Other formats</th>
</tr>
<tr>
<td width="60%"><div class="grid-layout grid-layout-for-text layout-tightest"><table>
<tr><td width="1%"><input type="radio" name="custom-tabular-exporter-format" value="tsv" /></td><td>Tab-separated values (TSV)</td></tr>
<tr><td width="1%"><input type="radio" name="custom-tabular-exporter-format" value="csv" /></td><td>Comma-separated values (CSV)</td></tr>
<tr><td width="1%"><input type="radio" name="custom-tabular-exporter-format" value="*sv" /></td><td>Custom separator
<input type="text" class="lightweight" size="5" bind="separatorInput" />
</td></tr>
</table></div></td>
<td><div class="grid-layout grid-layout-for-text layout-tightest"><table>
<tr>
<td width="1%"><input type="radio" name="custom-tabular-exporter-format" value="xls" /></td>
<td>Excel (.xls)</td>
</tr>
<tr>
<td width="1%"><input type="radio" name="custom-tabular-exporter-format" value="xlsx" /></td>
<td>Excel in XML (.xlsx)</td>
</tr>
<tr>
<td width="1%"><input type="radio" name="custom-tabular-exporter-format" value="html" /></td>
<td>HTML table</td>
</tr>
</table></div></td>
</tr>
<tr>
<td><div class="grid-layout grid-layout-for-text layout-tightest"><table>
<tr><td>Line separator</td><td><input type="text" size="5" class="lightweight" bind="lineSeparatorInput" /></td></tr>
<tr><td>Character encoding</td><td><input type="text" size="10" class="lightweight" bind="encodingInput" /></td></tr>
</table></div></td>
</tr>
</table></div></div>
<div id="custom-tabular-exporter-tabs-content"><div class="grid-layout grid-layout-for-ui layout-normal layout-full"><table>
<tr>
<td>Select and Order Columns to Export</td>
@ -109,22 +72,86 @@
</table></div></div></td>
</tr>
<tr>
<td>
<td colspan="2">
<button class="button" bind="selectAllButton">Select All</button>
<button class="button" bind="deselectAllButton">De-select All</button>
</td>
<td><div class="grid-layout layout-tighter"><table>
</tr>
<tr>
<td colspan="2"><div class="grid-layout layout-tighter layout-full"><table>
<tr>
<td width="1%"><input type="checkbox" bind="outputColumnHeadersCheckbox" checked /></td>
<td width="50%">Output column headers</td>
<td width="25%">Output column headers</td>
<td width="1%"><input type="checkbox" bind="outputBlankRowsCheckbox" /></td>
<td width="50%">Output blank rows</td>
<td width="25%">Output blank rows</td>
<td width="1%"><input type="checkbox" bind="exportAllRowsCheckbox" /></td>
<td width="50%">Ignore facets and filters and export all rows</td>
</tr>
</table></div></td>
</tr>
</table></div></div>
<div id="custom-tabular-exporter-tabs-download" style="display: none;"><div class="grid-layout grid-layout-for-ui layout-loose layout-full"><table>
<tr>
<th>Line-based text formats</th>
<th>Other formats</th>
</tr>
<tr>
<td width="60%"><div class="grid-layout grid-layout-for-text layout-tightest"><table>
<tr><td width="1%"><input type="radio" name="custom-tabular-exporter-download-format" value="tsv" checked /></td><td>Tab-separated values (TSV)</td></tr>
<tr><td width="1%"><input type="radio" name="custom-tabular-exporter-download-format" value="csv" /></td><td>Comma-separated values (CSV)</td></tr>
<tr><td width="1%"><input type="radio" name="custom-tabular-exporter-download-format" value="*sv" /></td><td>Custom separator
<input type="text" class="lightweight" size="5" bind="separatorInput" />
</td></tr>
</table></div></td>
<td><div class="grid-layout grid-layout-for-text layout-tightest"><table>
<tr>
<td width="1%"><input type="radio" name="custom-tabular-exporter-download-format" value="xls" /></td>
<td>Excel (.xls)</td>
</tr>
<tr>
<td width="1%"><input type="radio" name="custom-tabular-exporter-download-format" value="xlsx" /></td>
<td>Excel in XML (.xlsx)</td>
</tr>
<tr>
<td width="1%"><input type="radio" name="custom-tabular-exporter-download-format" value="html" /></td>
<td>HTML table</td>
</tr>
</table></div></td>
</tr>
<tr>
<td><div class="grid-layout grid-layout-for-text layout-tightest"><table>
<tr><td>Line separator</td><td><input type="text" size="5" class="lightweight" bind="lineSeparatorInput" /></td></tr>
<tr><td>Character encoding</td><td><input type="text" size="10" class="lightweight" bind="encodingInput" /></td></tr>
</table></div></td>
</tr>
<tr>
<td colspan="2"><div class="grid-layout grid-layout-for-text layout-tightest layout-full"><table><tr>
<td width="100%"> </td>
<td width="1%"><button class="button" bind="downloadPreviewButton">Preview</button></td>
<td width="1%"><button class="button button-primary" bind="downloadButton">Download</button></td>
</tr></table></div></td>
</tr>
</table></div></div>
<div bind="uploadTabBody" id="custom-tabular-exporter-tabs-upload" style="display: none;"><div class="grid-layout grid-layout-for-ui layout-loose layout-full"><table bind="uploadTargetTable">
<tr>
<th colspan="2">Upload to</th>
</tr>
<tr>
<td colspan="2"><div class="grid-layout grid-layout-for-text layout-tightest layout-full"><table><tr>
<td width="100%"> </td>
<td width="1%"><button class="button button-primary" bind="uploadButton">Upload</button></td>
</tr></table></div></td>
</tr>
</table></div></div>
<div id="custom-tabular-exporter-tabs-code" style="display: none;"><div class="grid-layout grid-layout-for-ui layout-loose layout-full"><table>
<tr>
<td>The following JSON text encodes the options you have set in the other tabs.
@ -139,11 +166,5 @@
</div>
<div class="dialog-footer" bind="dialogFooter"><div class="grid-layout layout-tightest layout-full"><table><tr>
<td><button class="button" bind="cancelButton">Cancel</button></td>
<td width="1%"><input type="checkbox" bind="exportAllRowsCheckbox" /></td>
<td>Ignore facets and filters and export all rows</td>
<td width="1%"><button class="button" bind="previewButton">Preview</button></td>
<td width="1%"><button class="button button-primary" bind="exportButton">Export</button></td>
</tr></table></div></div>
</div>

View File

@ -68,6 +68,8 @@ CustomTabularExporterDialog.formats = {
}
};
CustomTabularExporterDialog.uploadTargets = [];
CustomTabularExporterDialog.prototype._createDialog = function(options) {
var self = this;
@ -75,8 +77,14 @@ CustomTabularExporterDialog.prototype._createDialog = function(options) {
this._elmts = DOM.bind(this._dialog);
this._level = DialogSystem.showDialog(this._dialog);
$("#custom-tabular-exporter-tabs-format").css("display", "");
if (CustomTabularExporterDialog.uploadTargets.length == 0) {
this._elmts.uploadTabHeader.remove();
this._elmts.uploadTabBody.remove();
}
$("#custom-tabular-exporter-tabs-content").css("display", "");
$("#custom-tabular-exporter-tabs-download").css("display", "");
$("#custom-tabular-exporter-tabs-upload").css("display", "");
$("#custom-tabular-exporter-tabs-code").css("display", "");
$("#custom-tabular-exporter-tabs").tabs();
@ -116,6 +124,34 @@ CustomTabularExporterDialog.prototype._createDialog = function(options) {
}
this._elmts.columnList.sortable({});
/*
* Populate upload targets.
*/
if (CustomTabularExporterDialog.uploadTargets.length > 0) {
var table = this._elmts.uploadTargetTable[0];
for (var i = 0; i < CustomTabularExporterDialog.uploadTargets.length; i++) {
var target = CustomTabularExporterDialog.uploadTargets[i];
var tr = table.insertRow(table.rows.length - 1);
var td0 = $(tr.insertCell(0))
.attr('width', '1');
var input = $('<input>')
.attr('type', 'radio')
.attr('name', 'custom-tabular-exporter-upload-format')
.attr('value', target.id)
.appendTo(td0);
if (i === 0) {
input.attr('checked', 'checked');
}
$(tr.insertCell(1))
.attr('width', '100%')
.text(target.label);
}
this._elmts.uploadButton.click(function() { self._upload(); });
}
/*
* Hook up event handlers.
*/
@ -146,21 +182,21 @@ CustomTabularExporterDialog.prototype._createDialog = function(options) {
$('#custom-tabular-exporter-tabs-content').find('input').bind('change', function() {
self._updateOptionCode();
});
$('#custom-tabular-exporter-tabs-format').find('input').bind('change', function() {
$('#custom-tabular-exporter-tabs-download').find('input').bind('change', function() {
self._updateOptionCode();
});
this._elmts.applyOptionCodeButton.click(function(evt) { self._applyOptionCode(); });
this._elmts.cancelButton.click(function() { self._dismiss(); });
this._elmts.exportButton.click(function() { self._commit(); });
this._elmts.previewButton.click(function(evt) { self._preview(); });
this._elmts.downloadButton.click(function() { self._download(); });
this._elmts.downloadPreviewButton.click(function(evt) { self._previewDownload(); });
this._configureUIFromOptionCode(options);
this._updateOptionCode();
};
CustomTabularExporterDialog.prototype._configureUIFromOptionCode = function(options) {
this._dialog.find('input[name="custom-tabular-exporter-format"][value="' + options.format + '"]').attr('checked', 'checked');
this._dialog.find('input[name="custom-tabular-exporter-download-format"][value="' + options.format + '"]').attr('checked', 'checked');
this._elmts.separatorInput[0].value = String.encodeSeparator(options.separator || ',');
this._elmts.lineSeparatorInput[0].value = String.encodeSeparator(options.lineSeparator || '\n');
this._elmts.encodingInput[0].value = options.encoding;
@ -188,11 +224,11 @@ CustomTabularExporterDialog.prototype._dismiss = function() {
DialogSystem.dismissUntil(this._level - 1);
};
CustomTabularExporterDialog.prototype._preview = function() {
CustomTabularExporterDialog.prototype._previewDownload = function() {
this._postExport(true);
};
CustomTabularExporterDialog.prototype._commit = function() {
CustomTabularExporterDialog.prototype._download = function() {
this._postExport(false);
this._dismiss();
};
@ -290,7 +326,7 @@ CustomTabularExporterDialog.prototype._applyOptionCode = function() {
CustomTabularExporterDialog.prototype._getOptionCode = function() {
var options = {
format: this._dialog.find('input[name="custom-tabular-exporter-format"]:checked').val()
format: this._dialog.find('input[name="custom-tabular-exporter-download-format"]:checked').val()
};
if (options.format == 'tsv' || options.format == 'csv' || options.format == '*sv') {
@ -329,3 +365,22 @@ CustomTabularExporterDialog.prototype._getOptionCode = function() {
return options;
};
CustomTabularExporterDialog.prototype._upload = function() {
var id = this._dialog.find('input[name="custom-tabular-exporter-upload-format"]:checked').val()
for (var i = 0; i < CustomTabularExporterDialog.uploadTargets.length; i++) {
var target = CustomTabularExporterDialog.uploadTargets[i];
if (id == target.id) {
var self = this;
var options = this._getOptionCode();
options.format = id;
target.handler(
options,
this._elmts.exportAllRowsCheckbox[0].checked,
function() {
self._dismiss();
});
return;
}
}
};