Started working on new import UI. Not much to see yet, but if you append ?new=1 to the index page URL then you see the new form. It can only upload a file at the moment.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@1971 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
a81dcc50cc
commit
90794d5039
@ -50,6 +50,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.refine.commands.Command;
|
||||
import com.google.refine.commands.importing.ImportManager;
|
||||
import com.google.refine.io.FileProjectManager;
|
||||
|
||||
import edu.mit.simile.butterfly.Butterfly;
|
||||
@ -124,6 +125,7 @@ public class RefineServlet extends Butterfly {
|
||||
|
||||
s_dataDir = new File(data);
|
||||
FileProjectManager.initialize(s_dataDir);
|
||||
ImportManager.initialize(this);
|
||||
|
||||
if (_timer == null) {
|
||||
_timer = new Timer("autosave");
|
||||
|
@ -312,7 +312,7 @@ public abstract class Command {
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
String message,
|
||||
Exception e
|
||||
Throwable e
|
||||
) {
|
||||
VelocityContext context = new VelocityContext();
|
||||
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
package com.google.refine.commands.importing;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.refine.commands.Command;
|
||||
|
||||
public class CreateImportJobCommand extends Command {
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger("create-import-job_command");
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
long id = ImportManager.singleton().createJob().id;
|
||||
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setHeader("Content-Type", "application/json");
|
||||
respond(response, "{ \"jobID\" : " + id + " }");
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
package com.google.refine.commands.importing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.refine.commands.Command;
|
||||
import com.google.refine.commands.importing.ImportJob.State;
|
||||
|
||||
public class GetImportJobStatusCommand extends Command {
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger("get-import-job-status_command");
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
long jobID = Long.parseLong(request.getParameter("jobID"));
|
||||
ImportJob job = ImportManager.singleton().getJob(jobID);
|
||||
|
||||
Writer w = response.getWriter();
|
||||
JSONWriter writer = new JSONWriter(w);
|
||||
try {
|
||||
writer.object();
|
||||
if (job == null) {
|
||||
writer.key("code"); writer.value("error");
|
||||
writer.key("message"); writer.value("No such import job");
|
||||
} else {
|
||||
writer.key("code"); writer.value("ok");
|
||||
writer.key("state");
|
||||
if (job.state == State.NEW) {
|
||||
writer.value("new");
|
||||
} else if (job.state == State.RETRIEVING_DATA) {
|
||||
writer.value("retrieving");
|
||||
writer.key("progress"); writer.value(job.retrievingProgress);
|
||||
writer.key("bytesSaved"); writer.value(job.bytesSaved);
|
||||
} else if (job.state == State.READY) {
|
||||
writer.value("ready");
|
||||
} else if (job.state == State.ERROR) {
|
||||
writer.value("error");
|
||||
writer.key("message"); writer.value(job.errorMessage);
|
||||
if (job.exception != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
job.exception.printStackTrace(pw);
|
||||
pw.flush();
|
||||
sw.flush();
|
||||
|
||||
writer.key("stack"); writer.value(sw.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.endObject();
|
||||
} catch (JSONException e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
w.flush();
|
||||
w.close();
|
||||
}
|
||||
}
|
||||
}
|
49
main/src/com/google/refine/commands/importing/ImportJob.java
Normal file
49
main/src/com/google/refine/commands/importing/ImportJob.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.google.refine.commands.importing;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import com.google.refine.model.meta.ImportSource;
|
||||
|
||||
public class ImportJob {
|
||||
static public enum State {
|
||||
NEW,
|
||||
RETRIEVING_DATA,
|
||||
READY,
|
||||
ERROR
|
||||
}
|
||||
|
||||
final public long id;
|
||||
final public File dir;
|
||||
|
||||
public long lastTouched;
|
||||
public State state = State.NEW;
|
||||
|
||||
// Data for retrieving phase
|
||||
public int retrievingProgress = 0; // from 0 to 100
|
||||
public long bytesSaved = 0; // in case percentage is unknown
|
||||
public String errorMessage;
|
||||
public Throwable exception;
|
||||
|
||||
public ImportSource importSource;
|
||||
|
||||
public ImportJob(long id, File dir) {
|
||||
this.id = id;
|
||||
this.dir = dir;
|
||||
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
public void touch() {
|
||||
lastTouched = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
try {
|
||||
FileUtils.deleteDirectory(dir);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
101
main/src/com/google/refine/commands/importing/ImportManager.java
Normal file
101
main/src/com/google/refine/commands/importing/ImportManager.java
Normal file
@ -0,0 +1,101 @@
|
||||
package com.google.refine.commands.importing;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import com.google.refine.RefineServlet;
|
||||
import com.google.refine.model.meta.ImportSource;
|
||||
|
||||
public class ImportManager {
|
||||
static final private Map<String, Class<? extends ImportSource>> nameToImportSourceClass =
|
||||
new HashMap<String, Class<? extends ImportSource>>();
|
||||
|
||||
static final private Map<String, String> importSourceClassNameToName =
|
||||
new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Register a single import source class.
|
||||
*
|
||||
* @param name importer verb for importer
|
||||
* @param importerObject object implementing the importer
|
||||
*
|
||||
* @return true if importer was loaded and registered successfully
|
||||
*/
|
||||
static public boolean registerImportSourceClass(String name, Class<? extends ImportSource> klass) {
|
||||
if (nameToImportSourceClass.containsKey(name)) {
|
||||
return false;
|
||||
}
|
||||
nameToImportSourceClass.put(name, klass);
|
||||
importSourceClassNameToName.put(klass.getName(), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static public Class<? extends ImportSource> getImportSourceClass(String name) {
|
||||
return nameToImportSourceClass.get(name);
|
||||
}
|
||||
|
||||
static public String getImportSourceClassName(Class<? extends ImportSource> klass) {
|
||||
return importSourceClassNameToName.get(klass.getName());
|
||||
}
|
||||
|
||||
final private RefineServlet servlet;
|
||||
final private Map<Long, ImportJob> jobs = new HashMap<Long, ImportJob>();
|
||||
private File importDir;
|
||||
|
||||
static private ImportManager singleton;
|
||||
|
||||
static public void initialize(RefineServlet servlet) {
|
||||
singleton = new ImportManager(servlet);
|
||||
}
|
||||
|
||||
static public ImportManager singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
private ImportManager(RefineServlet servlet) {
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
private File getImportDir() {
|
||||
if (importDir == null) {
|
||||
File tempDir = servlet.getTempDir();
|
||||
importDir = tempDir == null ? new File(".import-temp") : new File(tempDir, "import");
|
||||
|
||||
if (importDir.exists()) {
|
||||
try {
|
||||
// start fresh
|
||||
FileUtils.deleteDirectory(importDir);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
importDir.mkdirs();
|
||||
}
|
||||
return importDir;
|
||||
}
|
||||
|
||||
public ImportJob createJob() {
|
||||
long id = System.currentTimeMillis() + (long) (Math.random() * 1000000);
|
||||
File jobDir = new File(getImportDir(), Long.toString(id));
|
||||
|
||||
ImportJob job = new ImportJob(id, jobDir);
|
||||
jobs.put(id, job);
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
public ImportJob getJob(long id) {
|
||||
return jobs.get(id);
|
||||
}
|
||||
|
||||
public void disposeJob(long id) {
|
||||
ImportJob job = getJob(id);
|
||||
if (job != null) {
|
||||
job.dispose();
|
||||
jobs.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
package com.google.refine.commands.importing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.refine.commands.Command;
|
||||
import com.google.refine.commands.importing.ImportJob.State;
|
||||
import com.google.refine.model.meta.ImportSource;
|
||||
import com.google.refine.util.ParsingUtilities;
|
||||
|
||||
public class RetrieveImportContentCommand extends Command {
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger("retrieve-import-content_command");
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
/*
|
||||
* The uploaded file is in the POST body as a "file part". If
|
||||
* we call request.getParameter() then the POST body will get
|
||||
* read and we won't have a chance to parse the body ourselves.
|
||||
* This is why we have to parse the URL for parameters ourselves.
|
||||
* Don't call request.getParameter() before calling internalImport().
|
||||
*/
|
||||
Properties options = ParsingUtilities.parseUrlParameters(request);
|
||||
|
||||
long jobID = Long.parseLong(options.getProperty("jobID"));
|
||||
ImportJob job = ImportManager.singleton().getJob(jobID);
|
||||
if (job == null) {
|
||||
respondWithErrorPage(request, response, "No such import job", null);
|
||||
return;
|
||||
} else if (job.state != State.NEW) {
|
||||
respondWithErrorPage(request, response, "Import job already started", null);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<? extends ImportSource> importSourceClass =
|
||||
ImportManager.getImportSourceClass(options.getProperty("source"));
|
||||
if (importSourceClass == null) {
|
||||
respondWithErrorPage(request, response, "No such import source class", null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ImportSource importSource = importSourceClass.newInstance();
|
||||
job.importSource = importSource;
|
||||
job.state = State.RETRIEVING_DATA;
|
||||
|
||||
importSource.retrieveContent(request, options, job);
|
||||
|
||||
job.retrievingProgress = 100;
|
||||
job.state = State.READY;
|
||||
} catch (Throwable e) {e.printStackTrace();
|
||||
job.state = State.ERROR;
|
||||
job.errorMessage = e.getLocalizedMessage();
|
||||
job.exception = e;
|
||||
|
||||
respondWithErrorPage(request, response, "Failed to kick start import job", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.google.refine.model.meta;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.fileupload.FileItemIterator;
|
||||
import org.apache.commons.fileupload.FileItemStream;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
|
||||
import com.google.refine.commands.importing.ImportJob;
|
||||
|
||||
public class FileUploadImportSource extends ImportSource {
|
||||
public String originalFileName;
|
||||
|
||||
@Override
|
||||
protected void customWrite(JSONWriter writer, Properties options)
|
||||
throws JSONException {
|
||||
writer.key("originalFileName"); writer.value(originalFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customReconstruct(JSONObject obj) throws JSONException {
|
||||
if (obj.has("originalFileName")) {
|
||||
originalFileName = obj.getString("originalFileName");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveContent(HttpServletRequest request, Properties options, ImportJob job) throws Exception {
|
||||
ServletFileUpload upload = new ServletFileUpload();
|
||||
FileItemIterator iter = upload.getItemIterator(request);
|
||||
while (iter.hasNext()) {
|
||||
FileItemStream item = iter.next();
|
||||
if (!item.isFormField()) {
|
||||
String fileName = item.getName();
|
||||
if (fileName.length() > 0) {
|
||||
InputStream stream = item.openStream();
|
||||
try {
|
||||
File file = new File(job.dir, "data");
|
||||
|
||||
this.accessTime = new Date();
|
||||
this.contentType = item.getContentType();
|
||||
this.encoding = request.getCharacterEncoding();
|
||||
this.originalFileName = fileName;
|
||||
this.size = saveStreamToFileOrDir(
|
||||
item.openStream(), file, this.contentType, fileName, job, request.getContentLength());
|
||||
this.isArchive = file.isDirectory();
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
main/src/com/google/refine/model/meta/ImportConfig.java
Normal file
5
main/src/com/google/refine/model/meta/ImportConfig.java
Normal file
@ -0,0 +1,5 @@
|
||||
package com.google.refine.model.meta;
|
||||
|
||||
public class ImportConfig {
|
||||
|
||||
}
|
167
main/src/com/google/refine/model/meta/ImportSource.java
Normal file
167
main/src/com/google/refine/model/meta/ImportSource.java
Normal file
@ -0,0 +1,167 @@
|
||||
package com.google.refine.model.meta;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.tools.bzip2.CBZip2InputStream;
|
||||
import org.apache.tools.tar.TarEntry;
|
||||
import org.apache.tools.tar.TarInputStream;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
|
||||
import com.google.refine.Jsonizable;
|
||||
import com.google.refine.commands.importing.ImportJob;
|
||||
import com.google.refine.commands.importing.ImportManager;
|
||||
import com.google.refine.util.ParsingUtilities;
|
||||
|
||||
abstract public class ImportSource implements Jsonizable {
|
||||
public Date accessTime;
|
||||
public long size;
|
||||
public boolean isArchive = false;
|
||||
|
||||
public String contentType;
|
||||
public String encoding;
|
||||
|
||||
@Override
|
||||
public void write(JSONWriter writer, Properties options)
|
||||
throws JSONException {
|
||||
writer.object();
|
||||
writer.key("type"); writer.value(ImportManager.getImportSourceClassName(this.getClass()));
|
||||
writer.key("accessTime"); writer.value(ParsingUtilities.dateToString(accessTime));
|
||||
writer.key("size"); writer.value(size);
|
||||
writer.key("isArchive"); writer.value(isArchive);
|
||||
writer.key("contentType"); writer.value(contentType);
|
||||
writer.key("encoding"); writer.value(encoding);
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
public void reconstruct(JSONObject obj) throws JSONException {
|
||||
if (obj.has("accessTime")) {
|
||||
accessTime = ParsingUtilities.stringToDate(obj.getString("accessTime"));
|
||||
}
|
||||
if (obj.has("size")) {
|
||||
size = obj.getLong("size");
|
||||
}
|
||||
if (obj.has("isArchive")) {
|
||||
isArchive = obj.getBoolean("isArchive");
|
||||
}
|
||||
if (obj.has("contentType")) {
|
||||
contentType = obj.getString("contentType");
|
||||
}
|
||||
if (obj.has("encoding")) {
|
||||
encoding = obj.getString("encoding");
|
||||
}
|
||||
customReconstruct(obj);
|
||||
}
|
||||
|
||||
abstract public void retrieveContent(HttpServletRequest request, Properties options, ImportJob job)
|
||||
throws Exception;
|
||||
|
||||
abstract protected void customWrite(JSONWriter writer, Properties options) throws JSONException;
|
||||
abstract protected void customReconstruct(JSONObject obj) throws JSONException;
|
||||
|
||||
static protected long saveStreamToFileOrDir(
|
||||
InputStream is,
|
||||
File file,
|
||||
String contentType,
|
||||
String fileNameOrUrl,
|
||||
ImportJob job,
|
||||
long expectedSize
|
||||
) throws IOException {
|
||||
InputStream archiveIS = null;
|
||||
if (fileNameOrUrl != null) {
|
||||
try {
|
||||
if (fileNameOrUrl.endsWith(".tar.gz") ||
|
||||
fileNameOrUrl.endsWith(".tar.gz.gz") ||
|
||||
fileNameOrUrl.endsWith(".tgz")) {
|
||||
archiveIS = new TarInputStream(new GZIPInputStream(is));
|
||||
} else if (fileNameOrUrl.endsWith(".tar.bz2")) {
|
||||
archiveIS = new TarInputStream(new CBZip2InputStream(is));
|
||||
} else if (fileNameOrUrl.endsWith(".tar")) {
|
||||
archiveIS = new TarInputStream(is);
|
||||
} else if (fileNameOrUrl.endsWith(".zip")) {
|
||||
archiveIS = new ZipInputStream(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
archiveIS = null;
|
||||
}
|
||||
}
|
||||
|
||||
job.bytesSaved = 0;
|
||||
if (archiveIS == null) {
|
||||
saveStreamToFile(is, file, job, true, expectedSize);
|
||||
} else {
|
||||
job.retrievingProgress = -1;
|
||||
|
||||
// NOTE(SM): unfortunately, java.io does not provide any generalized class for
|
||||
// archive-like input streams so while both TarInputStream and ZipInputStream
|
||||
// behave precisely the same, there is no polymorphic behavior so we have
|
||||
// to treat each instance explicitly... one of those times you wish you had
|
||||
// closures
|
||||
|
||||
if (archiveIS instanceof TarInputStream) {
|
||||
TarInputStream tis = (TarInputStream) archiveIS;
|
||||
TarEntry te;
|
||||
while ((te = tis.getNextEntry()) != null) {
|
||||
if (!te.isDirectory()) {
|
||||
saveStreamToFile(tis, new File(file, te.getName()), job, false, 0);
|
||||
}
|
||||
}
|
||||
} else if (archiveIS instanceof ZipInputStream) {
|
||||
ZipInputStream zis = (ZipInputStream) archiveIS;
|
||||
ZipEntry ze;
|
||||
long compressedSize = 0;
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
if (!ze.isDirectory()) {
|
||||
saveStreamToFile(zis, new File(file, ze.getName()), job, false, 0);
|
||||
|
||||
compressedSize += ze.getCompressedSize(); // this might be negative if not known
|
||||
if (compressedSize > 0) {
|
||||
job.retrievingProgress = (int) (compressedSize * 100 / expectedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return job.bytesSaved;
|
||||
}
|
||||
|
||||
static private void saveStreamToFile(
|
||||
InputStream is,
|
||||
File file,
|
||||
ImportJob job,
|
||||
boolean updateProgress,
|
||||
long expectedSize
|
||||
) throws IOException {
|
||||
byte data[] = new byte[4096];
|
||||
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(fos, data.length);
|
||||
|
||||
int count;
|
||||
while ((count = is.read(data, 0, data.length)) != -1) {
|
||||
bos.write(data, 0, count);
|
||||
|
||||
job.bytesSaved += count;
|
||||
if (updateProgress) {
|
||||
job.retrievingProgress = (int) (job.bytesSaved * 100 / expectedSize);
|
||||
}
|
||||
}
|
||||
|
||||
bos.flush();
|
||||
bos.close();
|
||||
}
|
||||
}
|
28
main/src/com/google/refine/model/meta/TextImportSource.java
Normal file
28
main/src/com/google/refine/model/meta/TextImportSource.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.google.refine.model.meta;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
|
||||
import com.google.refine.commands.importing.ImportJob;
|
||||
|
||||
public class TextImportSource extends ImportSource {
|
||||
@Override
|
||||
protected void customWrite(JSONWriter writer, Properties options)
|
||||
throws JSONException {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customReconstruct(JSONObject obj) throws JSONException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveContent(HttpServletRequest request, Properties options, ImportJob job) throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
34
main/src/com/google/refine/model/meta/WebImportSource.java
Normal file
34
main/src/com/google/refine/model/meta/WebImportSource.java
Normal file
@ -0,0 +1,34 @@
|
||||
package com.google.refine.model.meta;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
|
||||
import com.google.refine.commands.importing.ImportJob;
|
||||
|
||||
public class WebImportSource extends ImportSource {
|
||||
public String url;
|
||||
|
||||
@Override
|
||||
protected void customWrite(JSONWriter writer, Properties options)
|
||||
throws JSONException {
|
||||
writer.key("url"); writer.value(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customReconstruct(JSONObject obj) throws JSONException {
|
||||
if (obj.has("url")) {
|
||||
url = obj.getString("url");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveContent(HttpServletRequest request, Properties options, ImportJob job) throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
@ -34,14 +34,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
var html = "text/html";
|
||||
var encoding = "UTF-8";
|
||||
var ClientSideResourceManager = Packages.com.google.refine.ClientSideResourceManager;
|
||||
var bundle = true;
|
||||
var bundle = false;
|
||||
|
||||
var templatedFiles = {
|
||||
// Requests with last path segments mentioned here
|
||||
// will get served from .vt files with the same names
|
||||
"import" : true,
|
||||
"index" : true,
|
||||
"project" : true,
|
||||
"preferences" : true
|
||||
"preferences" : true,
|
||||
"project" : true
|
||||
};
|
||||
|
||||
function registerCommands() {
|
||||
@ -49,6 +50,10 @@ function registerCommands() {
|
||||
|
||||
RS.registerCommand(module, "get-version", new Packages.com.google.refine.commands.GetVersionCommand());
|
||||
|
||||
RS.registerCommand(module, "create-import-job", new Packages.com.google.refine.commands.importing.CreateImportJobCommand());
|
||||
RS.registerCommand(module, "retrieve-import-content", new Packages.com.google.refine.commands.importing.RetrieveImportContentCommand());
|
||||
RS.registerCommand(module, "get-import-job-status", new Packages.com.google.refine.commands.importing.GetImportJobStatusCommand());
|
||||
|
||||
RS.registerCommand(module, "create-project-from-upload", new Packages.com.google.refine.commands.project.CreateProjectCommand());
|
||||
RS.registerCommand(module, "import-project", new Packages.com.google.refine.commands.project.ImportProjectCommand());
|
||||
RS.registerCommand(module, "export-project", new Packages.com.google.refine.commands.project.ExportProjectCommand());
|
||||
@ -160,6 +165,13 @@ function registerOperations() {
|
||||
OR.registerOperation(module, "recon-copy-across-columns", Packages.com.google.refine.operations.recon.ReconCopyAcrossColumnsOperation);
|
||||
}
|
||||
|
||||
function registerImportSourceClasses() {
|
||||
var RM = Packages.com.google.refine.commands.importing.ImportManager;
|
||||
RM.registerImportSourceClass("file-upload", Packages.com.google.refine.model.meta.FileUploadImportSource);
|
||||
RM.registerImportSourceClass("text", Packages.com.google.refine.model.meta.TextImportSource);
|
||||
RM.registerImportSourceClass("web", Packages.com.google.refine.model.meta.WebImportSource);
|
||||
}
|
||||
|
||||
/*
|
||||
* This optional function is invoked from the module's init() Java function.
|
||||
*/
|
||||
@ -168,6 +180,7 @@ function init() {
|
||||
|
||||
registerCommands();
|
||||
registerOperations();
|
||||
registerImportSourceClasses();
|
||||
|
||||
var RC = Packages.com.google.refine.model.recon.ReconConfig;
|
||||
RC.registerReconConfig(module, "standard-service", Packages.com.google.refine.model.recon.StandardReconConfig);
|
||||
@ -180,7 +193,9 @@ function init() {
|
||||
"externals/jquery-ui/jquery-ui-1.8.custom.min.js",
|
||||
"externals/date.js",
|
||||
"scripts/util/string.js",
|
||||
"scripts/index.js"
|
||||
"scripts/util/dom.js",
|
||||
"scripts/index.js",
|
||||
"scripts/index/import-sources.js"
|
||||
]
|
||||
);
|
||||
|
||||
@ -196,6 +211,31 @@ function init() {
|
||||
]
|
||||
);
|
||||
|
||||
ClientSideResourceManager.addPaths(
|
||||
"import/scripts",
|
||||
module,
|
||||
[
|
||||
"externals/jquery-1.4.2.min.js",
|
||||
"externals/jquery-ui/jquery-ui-1.8.custom.min.js",
|
||||
"externals/date.js",
|
||||
"scripts/util/string.js",
|
||||
"scripts/util/dom.js",
|
||||
"scripts/import.js"
|
||||
]
|
||||
);
|
||||
|
||||
ClientSideResourceManager.addPaths(
|
||||
"import/styles",
|
||||
module,
|
||||
[
|
||||
"externals/jquery-ui/css/ui-lightness/jquery-ui-1.8.custom.css",
|
||||
"styles/jquery-ui-overrides.less",
|
||||
"styles/common.less",
|
||||
"styles/pure.css",
|
||||
"styles/import.less"
|
||||
]
|
||||
);
|
||||
|
||||
ClientSideResourceManager.addPaths(
|
||||
"project/scripts",
|
||||
module,
|
||||
@ -382,6 +422,14 @@ function process(path, request, response) {
|
||||
} else {
|
||||
if (lastSegment in templatedFiles) {
|
||||
var context = {};
|
||||
|
||||
var params = new Packages.java.util.Properties();
|
||||
var e = request.getParameterNames();
|
||||
while (e.hasMoreElements()) {
|
||||
var name = e.nextElement();
|
||||
params.put(name, request.getParameterValues(name)[0]);
|
||||
}
|
||||
context.params = params;
|
||||
context.projectID = request.getParameter("project");
|
||||
|
||||
var styles = ClientSideResourceManager.getPaths(lastSegment + "/styles");
|
||||
|
61
main/webapp/modules/core/import.vt
Normal file
61
main/webapp/modules/core/import.vt
Normal file
@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Google Refine</title>
|
||||
<script type="text/javascript" src="wirings.js"></script>
|
||||
<link rel="icon" type="image/png" href="images/favicon.png">
|
||||
$scriptInjection
|
||||
$styleInjection
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<a id="app-home-button" href="./"><img alt="Google Refine" src="images/logo-googlerefine-30.png" width="129" height="29" /></a>
|
||||
<div id="project-title">
|
||||
Project Name: <span id="project-name-button" class="app-path-section" title="Click to rename project">Untitled</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="body">
|
||||
<div bind="topPanelDiv" id="top-panel">
|
||||
<h1>Preview</h1>
|
||||
</div>
|
||||
<div bind="middlePanelDiv" id="middle-panel">
|
||||
</div>
|
||||
<div bind="bottomPanelDiv" id="bottom-panel">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -36,10 +36,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
<meta charset="utf-8">
|
||||
<title>Google Refine</title>
|
||||
<link rel="icon" type="image/png" href="images/favicon.png">
|
||||
<script type="text/javascript" src="wirings.js"></script>
|
||||
$scriptInjection
|
||||
$styleInjection
|
||||
</head>
|
||||
<body>
|
||||
#if($params.new == "1")
|
||||
#set($newStyle = "")
|
||||
#set($oldStyle = "display: none; ")
|
||||
#else
|
||||
#set($oldStyle = "")
|
||||
#set($newStyle = "display: none; ")
|
||||
#end
|
||||
<div id="container">
|
||||
<div id="logo"> </div>
|
||||
<div id="header-home">
|
||||
@ -58,7 +66,50 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
<div class="content-block-footer"><a href="javascript:openWorkspaceDir()" class="secondary">Browse workspace directory</a></div>
|
||||
</div>
|
||||
<div id="project-create">
|
||||
<form id="file-upload-form" method="post" enctype="multipart/form-data" action="/command/core/create-project-from-upload" accept-charset="UTF-8">
|
||||
<h1 style="$newStyle">Create a New Project</h1>
|
||||
<div style="$newStyle" id="import-panel"><table id="import-panel-layout">
|
||||
<tr>
|
||||
<td id="import-panel-tab-headers">
|
||||
<div>Import data from</div>
|
||||
</td>
|
||||
<td id="import-panel-tab-bodies"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" id="import-panel-message">
|
||||
<h3>What kinds of data files can I import?</h3>
|
||||
<div>TSV, CSV, *SV, Excel (.xls and .xlsx), JSON, XML, RDF as XML, and
|
||||
Google Spreadsheets are all supported. Support for other formats can
|
||||
be added with Refine extensions.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table></div>
|
||||
|
||||
<div style="$newStyle" id="import-progress-panel">
|
||||
<div class="grid-layout layout-normal layout-full"><table>
|
||||
<tr><td colspan="3" id="import-progress-message"></td></tr>
|
||||
<tr><td colspan="3">
|
||||
<div id="import-progress-bar-frame"><div id="import-progress-bar-body"></div></div>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td id="import-progress-message-left"></td>
|
||||
<td id="import-progress-message-center"></td>
|
||||
<td id="import-progress-message-right"></td>
|
||||
</tr>
|
||||
<tr><td colspan="3">
|
||||
<button class="button" id="import-progress-cancel-button">Cancel</button>
|
||||
</td></tr>
|
||||
</table></div>
|
||||
<iframe id="import-iframe" name="import-iframe"></iframe>
|
||||
</div>
|
||||
|
||||
<div style="$newStyle" id="import-error-panel"><div class="grid-layout layout-normal layout-full"><table>
|
||||
<tr><td id="import-error-message"></td></tr>
|
||||
<tr><td id="import-error-stack"></td></tr>
|
||||
<tr><td><button class="button button-primary" id="import-error-ok-button">OK</button></td></tr>
|
||||
</table></div></div>
|
||||
|
||||
<form style="$oldStyle" id="file-upload-form" method="post" enctype="multipart/form-data" action="/command/core/create-project-from-upload" accept-charset="UTF-8">
|
||||
<h1>Create a New Project</h1>
|
||||
<h2 id="project-toggle">
|
||||
<a class="secondary" href="javascript:showHide('file-upload-form', 'project-upload-form')">or Import an Existing Project</a>
|
||||
@ -132,7 +183,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="project-upload-form" method="post" enctype="multipart/form-data" action="/command/core/import-project" accept-charset="UTF-8" style="display:none;">
|
||||
<form style="display: none;" id="project-upload-form" method="post" enctype="multipart/form-data" action="/command/core/import-project" accept-charset="UTF-8" style="display:none;">
|
||||
<h1>Import an Existing Project</h1>
|
||||
<h2 id="project-toggle">
|
||||
<a class="secondary" href="javascript:showHide('project-upload-form', 'file-upload-form')">or Create a New Project</a>
|
||||
|
53
main/webapp/modules/core/scripts/import.js
Normal file
53
main/webapp/modules/core/scripts/import.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
var theImportJob = {};
|
||||
var ui = {};
|
||||
|
||||
var Refine = {
|
||||
};
|
||||
|
||||
function resize() {
|
||||
var header = $("#header");
|
||||
|
||||
var leftPanelWidth = 300;
|
||||
var width = $(window).width();
|
||||
var top = $("#header").outerHeight();
|
||||
var height = $(window).height() - top;
|
||||
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
$(window).bind("resize", resize);
|
||||
}
|
||||
$(onLoad);
|
@ -281,7 +281,149 @@ function showVersion() {
|
||||
);
|
||||
}
|
||||
|
||||
function renderImportPanel() {
|
||||
var headerContainer = $('#import-panel-tab-headers');
|
||||
var bodyContainer = $('#import-panel-tab-bodies');
|
||||
|
||||
var selectImportSourceTab = function(importSource) {
|
||||
$('.import-panel-tab-body').hide();
|
||||
$('.import-panel-tab-header').removeClass('selected');
|
||||
|
||||
importSource._divBody.show();
|
||||
importSource._divHeader.addClass('selected');
|
||||
importSource._ui.focus();
|
||||
};
|
||||
|
||||
var createImportSourceTab = function(importSource) {
|
||||
importSource._divBody = $('<div>')
|
||||
.addClass('import-panel-tab-body')
|
||||
.appendTo(bodyContainer)
|
||||
.hide();
|
||||
|
||||
importSource._divHeader = $('<div>')
|
||||
.addClass('import-panel-tab-header')
|
||||
.text(importSource.label)
|
||||
.appendTo(headerContainer)
|
||||
.click(function() { selectImportSourceTab(importSource); });
|
||||
|
||||
importSource._ui = new importSource.ui(importSource._divBody);
|
||||
};
|
||||
|
||||
for (var i= 0; i < ImportSources.length; i++) {
|
||||
createImportSourceTab(ImportSources[i]);
|
||||
}
|
||||
selectImportSourceTab(ImportSources[0]);
|
||||
}
|
||||
|
||||
function startImportJob(importSource, form, progressMessage) {
|
||||
$.post(
|
||||
"/command/core/create-import-job",
|
||||
null,
|
||||
function(data) {
|
||||
var jobID = data.jobID;
|
||||
|
||||
form.attr("method", "post")
|
||||
.attr("enctype", "multipart/form-data")
|
||||
.attr("accept-charset", "UTF-8")
|
||||
.attr("target", "import-iframe")
|
||||
.attr("action", "/command/core/retrieve-import-content?" + $.param({
|
||||
"jobID" : jobID,
|
||||
"source" : importSource
|
||||
}));
|
||||
|
||||
form[0].submit();
|
||||
|
||||
var start = new Date();
|
||||
var timerID = window.setInterval(function() { pollImportJob(start, jobID, timerID); }, 1000);
|
||||
initializeImportProgressPanel(progressMessage, jobID, timerID);
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
function initializeImportProgressPanel(progressMessage, jobID, timerID) {
|
||||
$('#import-progress-message').text(progressMessage);
|
||||
$('#import-progress-bar-body').css("width", "0%");
|
||||
$('#import-progress-message-left').text('Starting');
|
||||
$('#import-progress-message-center').empty();
|
||||
$('#import-progress-message-right').empty();
|
||||
|
||||
$('#import-panel').hide();
|
||||
$('#import-progress-panel').show();
|
||||
|
||||
$('#import-progress-cancel-button').unbind().click(function() {
|
||||
$('#import-panel').show();
|
||||
$('#import-progress-panel').hide();
|
||||
|
||||
// stop the iframe
|
||||
$('#import-iframe')[0].contentWindow.stop();
|
||||
|
||||
// stop the timed polling
|
||||
window.clearInterval(timerID);
|
||||
|
||||
// explicitly cancel the import job
|
||||
$.post("/command/core/cancel-import-job?" + $.param({ "jobID" : jobID }));
|
||||
});
|
||||
}
|
||||
|
||||
function bytesToString(b) {
|
||||
if (b >= 1024 * 1024) {
|
||||
return Math.round(b / (1024 * 1024)) + " MB";
|
||||
} else if (b >= 1024) {
|
||||
return Math.round(b / 1024) + " KB";
|
||||
} else {
|
||||
return b + " bytes";
|
||||
}
|
||||
}
|
||||
|
||||
function pollImportJob(start, jobID, timerID) {
|
||||
$.post(
|
||||
"/command/core/get-import-job-status?" + $.param({ "jobID" : jobID }),
|
||||
null,
|
||||
function(data) {
|
||||
if (data.code == "error") {
|
||||
showImportJobError(data.message);
|
||||
window.clearInterval(timerID);
|
||||
} else if (data.state == "error") {
|
||||
showImportJobError(data.message, data.stack);
|
||||
window.clearInterval(timerID);
|
||||
} else if (data.state == "retrieving") {
|
||||
if (data.progress < 0) {
|
||||
$('#import-progress-message-left').text(bytesToString(data.bytesSaved) + " saved");
|
||||
} else {
|
||||
$('#import-progress-bar-body').css("width", data.progress + "%");
|
||||
$('#import-progress-message-left').text(data.progress + "% saved");
|
||||
}
|
||||
} else if (data.state == "ready") {
|
||||
window.clearInterval(timerID);
|
||||
|
||||
// Just so if the user clicks Back the progress panel won't be showing if the DOM is cached.
|
||||
$('#import-progress-panel').hide();
|
||||
$('#import-panel').show();
|
||||
|
||||
window.location = "/import?" + $.param({ "jobID" : jobID });
|
||||
}
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
function showImportJobError(message, stack) {
|
||||
$('#import-error-message').text(message);
|
||||
$('#import-error-stack').text(stack || 'No technical details.');
|
||||
|
||||
$('#import-progress-panel').hide();
|
||||
$('#import-error-panel').show();
|
||||
|
||||
$('#import-error-ok-button').unbind().click(function() {
|
||||
$('#import-error-panel').hide();
|
||||
$('#import-panel').show();
|
||||
});
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
renderImportPanel();
|
||||
|
||||
fetchProjects();
|
||||
|
||||
$("#project-file-input").change(function() {
|
||||
|
@ -0,0 +1,7 @@
|
||||
<form bind="form"><div class="grid-layout layout-normal"><table>
|
||||
<tr><td>File to import:</td></tr>
|
||||
<tr><td><input type="file" bind="fileInput" name="project-file" />
|
||||
<input type="hidden" name="project-name" bind="nameInput" />
|
||||
</td></tr>
|
||||
<tr><td><button bind="nextButton" class="button button-primary" type="button">Next »</button></td></tr>
|
||||
</table></div></form>
|
82
main/webapp/modules/core/scripts/index/import-sources.js
Normal file
82
main/webapp/modules/core/scripts/index/import-sources.js
Normal 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.
|
||||
|
||||
*/
|
||||
|
||||
var ImportSources = [];
|
||||
|
||||
function ThisComputerImportSourceUI(bodyDiv) {
|
||||
bodyDiv.html(DOM.loadHTML("core", "scripts/index/import-from-computer-form.html"));
|
||||
|
||||
var elmts = DOM.bind(bodyDiv);
|
||||
elmts.nextButton.click(function(evt) {
|
||||
if (elmts.fileInput[0].files.length === 0) {
|
||||
window.alert("You must specify a data file to import.");
|
||||
} else {
|
||||
elmts.nameInput[0].value = elmts.fileInput[0].files[0].fileName
|
||||
.replace(/\.\w+/, "").replace(/[_-]/g, " ");
|
||||
|
||||
startImportJob("file-upload", elmts.form, "Uploading data file ...");
|
||||
}
|
||||
});
|
||||
}
|
||||
ImportSources.push({
|
||||
"label" : "This Computer",
|
||||
"ui" : ThisComputerImportSourceUI
|
||||
});
|
||||
|
||||
ThisComputerImportSourceUI.prototype.focus = function() {
|
||||
|
||||
}
|
||||
|
||||
function UrlImportSourceUI(bodyDiv) {
|
||||
|
||||
}
|
||||
ImportSources.push({
|
||||
"label" : "Web Address (URL)",
|
||||
"ui" : UrlImportSourceUI
|
||||
});
|
||||
|
||||
UrlImportSourceUI.prototype.focus = function() {
|
||||
|
||||
}
|
||||
|
||||
function ClipboardImportSourceUI(bodyDiv) {
|
||||
|
||||
}
|
||||
ImportSources.push({
|
||||
"label" : "Clipboard",
|
||||
"ui" : ClipboardImportSourceUI
|
||||
});
|
||||
|
||||
ClipboardImportSourceUI.prototype.focus = function() {
|
||||
|
||||
}
|
77
main/webapp/modules/core/styles/import.less
vendored
Normal file
77
main/webapp/modules/core/styles/import.less
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
@import-less url("theme.less");
|
||||
|
||||
#project-title {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 140px;
|
||||
height: 40px;
|
||||
padding: 4px 0 0 0;
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
#project-name-button {
|
||||
padding: 3px;
|
||||
background: @fill_editable;
|
||||
border: 1px solid #ccc;
|
||||
border-top: 1px solid #aaa;
|
||||
}
|
||||
|
||||
#project-name-button:hover {
|
||||
}
|
||||
|
||||
#body {
|
||||
position: relative;
|
||||
margin: @padding_loose;
|
||||
background: @fill_primary;
|
||||
border: 1px solid @chrome_primary;
|
||||
border-radius: @padding_tight;
|
||||
}
|
||||
|
||||
#top-panel {
|
||||
padding: @padding_loose;
|
||||
}
|
||||
|
||||
#middle-panel {
|
||||
border-top: 1px solid @chrome_primary;
|
||||
border-bottom: 1px solid @chrome_primary;
|
||||
background: white;
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#bottom-panel {
|
||||
height: 200px;
|
||||
}
|
@ -165,4 +165,97 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#import-panel {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
#import-panel-message {
|
||||
padding: @padding_loose;
|
||||
}
|
||||
|
||||
#import-panel-layout {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#import-panel-layout > tbody > tr > td {
|
||||
border-top: 1px solid @chrome_primary;
|
||||
border-bottom: 1px solid @chrome_primary;
|
||||
}
|
||||
|
||||
#import-panel-tab-bodies {
|
||||
background: white;
|
||||
}
|
||||
|
||||
#import-panel-tab-headers {
|
||||
width: 15em;
|
||||
background: @fill_primary;
|
||||
border-right: 1px solid @chrome_primary;
|
||||
padding-left: @padding_normal;
|
||||
padding-bottom: @padding_looser;
|
||||
}
|
||||
#import-panel-tab-headers > div {
|
||||
padding: @padding_tight;
|
||||
padding-right: @padding_normal;
|
||||
}
|
||||
|
||||
.import-panel-tab-body {
|
||||
padding: @padding_loose;
|
||||
}
|
||||
|
||||
.import-panel-tab-header {
|
||||
cursor: pointer;
|
||||
color: @link_primary;
|
||||
}
|
||||
.import-panel-tab-header.selected {
|
||||
cursor: default;
|
||||
color: black;
|
||||
background: white;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
left: 1px;
|
||||
border: 1px solid @chrome_primary;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
#import-progress-panel {
|
||||
display: none;
|
||||
font-size: 1.3em;
|
||||
padding: @padding_loose;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#import-progress-bar-frame {
|
||||
border: 1px solid @chrome_primary;
|
||||
padding: @padding_tighter;
|
||||
}
|
||||
|
||||
#import-progress-bar-body {
|
||||
background: @chrome_primary;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
#import-iframe {
|
||||
position: fixed;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
left: -300px;
|
||||
top: -300px;
|
||||
}
|
||||
|
||||
#import-error-panel {
|
||||
display: none;
|
||||
font-size: 1.3em;
|
||||
padding: @padding_loose;
|
||||
}
|
||||
#import-error-message {
|
||||
}
|
||||
#import-error-stack {
|
||||
font-family: monospace;
|
||||
whitespace: pre;
|
||||
padding: @padding_normal;
|
||||
border: 1px solid @chrome_primary;
|
||||
}
|
Loading…
Reference in New Issue
Block a user