Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
appinventor-sources
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
appinventor-sources
Commits
127c9611
Unverified
Commit
127c9611
authored
Feb 26, 2020
by
Bart Mathijssen
Committed by
GitHub
Feb 26, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow for a subset of projects to be exported (#2051)
Co-authored-by:
Aaron Traylor
<
attraylor@gmail.com
>
parent
83eacf43
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
131 additions
and
9 deletions
+131
-9
appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
...engine/src/com/google/appinventor/client/OdeMessages.java
+4
-0
appinventor/appengine/src/com/google/appinventor/client/TopToolbar.java
...pengine/src/com/google/appinventor/client/TopToolbar.java
+24
-5
appinventor/appengine/src/com/google/appinventor/client/tracking/Tracking.java
.../src/com/google/appinventor/client/tracking/Tracking.java
+2
-0
appinventor/appengine/src/com/google/appinventor/server/DownloadServlet.java
...ne/src/com/google/appinventor/server/DownloadServlet.java
+14
-4
appinventor/appengine/src/com/google/appinventor/server/FileExporter.java
...ngine/src/com/google/appinventor/server/FileExporter.java
+14
-0
appinventor/appengine/src/com/google/appinventor/server/FileExporterImpl.java
...e/src/com/google/appinventor/server/FileExporterImpl.java
+67
-0
appinventor/appengine/src/com/google/appinventor/shared/rpc/ServerLayout.java
...e/src/com/google/appinventor/shared/rpc/ServerLayout.java
+6
-0
No files found.
appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
View file @
127c9611
...
...
@@ -476,6 +476,10 @@ public interface OdeMessages extends Messages, AutogeneratedOdeMessages {
@Description
(
"Name of Export Project menuitem"
)
String
exportProjectMenuItem
();
@DefaultMessage
(
"Export {0} selected projects"
)
@Description
(
"Name of Export Selected Projects menuitem"
)
String
exportSelectedProjectsMenuItem
(
int
numSelectedProjects
);
@DefaultMessage
(
"Export all projects"
)
@Description
(
"Name of Export all Project menuitem"
)
String
exportAllProjectsMenuItem
();
...
...
appinventor/appengine/src/com/google/appinventor/client/TopToolbar.java
View file @
127c9611
...
...
@@ -186,8 +186,11 @@ public class TopToolbar extends Composite {
boolean
allowDelete
=
!
isReadOnly
&&
numSelectedProjects
>
0
;
boolean
allowExport
=
numSelectedProjects
>
0
;
boolean
allowExportAll
=
numProjects
>
0
;
String
exportProjectLabel
=
numSelectedProjects
>
1
?
MESSAGES
.
exportSelectedProjectsMenuItem
(
numSelectedProjects
)
:
MESSAGES
.
exportProjectMenuItem
();
fileDropDown
.
setItemHtmlById
(
WIDGET_NAME_EXPORTPROJECT
,
exportProjectLabel
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
deleteProjectMenuItem
(),
allowDelete
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
exportProjectMenuItem
()
,
allowExport
);
fileDropDown
.
setItemEnabled
ById
(
WIDGET_NAME_EXPORTPROJECT
,
allowExport
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
exportAllProjectsMenuItem
(),
allowExportAll
);
}
...
...
@@ -572,6 +575,8 @@ public class TopToolbar extends Composite {
//If we are in the projects view
if
(
selectedProjects
.
size
()
==
1
)
{
exportProject
(
selectedProjects
.
get
(
0
));
}
else
if
(
selectedProjects
.
size
()
>
1
)
{
exportSelectedProjects
(
selectedProjects
);
}
else
{
// The user needs to select only one project.
ErrorReporter
.
reportInfo
(
MESSAGES
.
wrongNumberProjectsSelected
());
...
...
@@ -589,6 +594,20 @@ public class TopToolbar extends Composite {
Downloader
.
getInstance
().
download
(
ServerLayout
.
DOWNLOAD_SERVLET_BASE
+
ServerLayout
.
DOWNLOAD_PROJECT_SOURCE
+
"/"
+
project
.
getProjectId
());
}
private
void
exportSelectedProjects
(
List
<
Project
>
projects
)
{
Tracking
.
trackEvent
(
Tracking
.
PROJECT_EVENT
,
Tracking
.
PROJECT_ACTION_DOWNLOAD_SELECTED_PROJECTS_SOURCE_YA
);
String
selectedProjPath
=
ServerLayout
.
DOWNLOAD_SERVLET_BASE
+
ServerLayout
.
DOWNLOAD_SELECTED_PROJECTS_SOURCE
+
"/"
;
for
(
Project
project
:
projects
)
{
selectedProjPath
+=
project
.
getProjectId
()
+
"-"
;
}
Downloader
.
getInstance
().
download
(
selectedProjPath
);
}
}
private
static
class
ExportAllProjectsAction
implements
Command
{
...
...
@@ -1069,7 +1088,7 @@ public class TopToolbar extends Composite {
Ode
.
getInstance
().
getProjectManager
().
getProjects
()
==
null
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
exportAllProjectsMenuItem
(),
Ode
.
getInstance
().
getProjectManager
().
getProjects
().
size
()
>
0
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
exportProjectMenuItem
()
,
false
);
fileDropDown
.
setItemEnabled
ById
(
WIDGET_NAME_EXPORTPROJECT
,
false
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
saveMenuItem
(),
false
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
saveAsMenuItem
(),
false
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
checkpointMenuItem
(),
false
);
...
...
@@ -1079,7 +1098,7 @@ public class TopToolbar extends Composite {
fileDropDown
.
setItemEnabled
(
MESSAGES
.
deleteProjectButton
(),
true
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
exportAllProjectsMenuItem
(),
Ode
.
getInstance
().
getProjectManager
().
getProjects
().
size
()
>
0
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
exportProjectMenuItem
()
,
true
);
fileDropDown
.
setItemEnabled
ById
(
WIDGET_NAME_EXPORTPROJECT
,
true
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
saveMenuItem
(),
true
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
saveAsMenuItem
(),
true
);
fileDropDown
.
setItemEnabled
(
MESSAGES
.
checkpointMenuItem
(),
true
);
...
...
appinventor/appengine/src/com/google/appinventor/client/tracking/Tracking.java
View file @
127c9611
...
...
@@ -43,6 +43,8 @@ public class Tracking {
"DownloadProjectSource-YA"
;
public
static
final
String
PROJECT_ACTION_DOWNLOAD_FILE_YA
=
PROJECT_ACTION_PREFIX
+
"DownloadFile-YA"
;
public
static
final
String
PROJECT_ACTION_DOWNLOAD_SELECTED_PROJECTS_SOURCE_YA
=
PROJECT_ACTION_PREFIX
+
"DownloadSelectedProjectsSource-YA"
;
public
static
final
String
PROJECT_ACTION_DOWNLOAD_ALL_PROJECTS_SOURCE_YA
=
PROJECT_ACTION_PREFIX
+
"DownloadAllProjectsSource-YA"
;
public
static
final
String
PROJECT_ACTION_SAVE_YA
=
PROJECT_ACTION_PREFIX
+
...
...
appinventor/appengine/src/com/google/appinventor/server/DownloadServlet.java
View file @
127c9611
...
...
@@ -20,6 +20,8 @@ import javax.servlet.ServletOutputStream;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.NoSuchElementException
;
import
java.util.logging.Logger
;
...
...
@@ -42,7 +44,7 @@ public class DownloadServlet extends OdeServlet {
// Constants for accessing split URI
/*
* Download kind can be: "project-output", "project-source",
* "all-projects-source", "file", or "userfile".
* "
selected-projects-source", "
all-projects-source", "file", or "userfile".
* Constants for these are defined in ServerLayout.
*/
private
static
final
int
DOWNLOAD_KIND_INDEX
=
3
;
...
...
@@ -164,7 +166,7 @@ public class DownloadServlet extends OdeServlet {
projectName
=
storageIo
.
getProjectName
(
projectUserId
,
projectId
);
}
catch
(
NumberFormatException
e
)
{
// assume we got a name instead
for
(
Long
pid:
storageIo
.
getProjects
(
projectUserId
))
{
for
(
Long
pid
:
storageIo
.
getProjects
(
projectUserId
))
{
if
(
storageIo
.
getProjectName
(
projectUserId
,
pid
).
equals
(
projectIdOrName
))
{
projectId
=
pid
;
}
...
...
@@ -186,7 +188,15 @@ public class DownloadServlet extends OdeServlet {
ProjectSourceZip
zipFile
=
fileExporter
.
exportProjectSourceZip
(
projectUserId
,
projectId
,
/* include history*/
true
,
/* include keystore */
true
,
zipName
,
true
,
true
,
false
,
false
);
downloadableFile
=
zipFile
.
getRawFile
();
}
else
if
(
downloadKind
.
equals
(
ServerLayout
.
DOWNLOAD_SELECTED_PROJECTS_SOURCE
))
{
String
[]
projectIdStrings
=
uriComponents
[
PROJECT_ID_INDEX
].
split
(
"-"
);
List
<
Long
>
projectIds
=
new
ArrayList
<
Long
>();
for
(
String
projectId
:
projectIdStrings
)
{
projectIds
.
add
(
Long
.
valueOf
(
projectId
));
}
ProjectSourceZip
zipFile
=
fileExporter
.
exportSelectedProjectsSourceZip
(
userId
,
"selected-projects.zip"
,
projectIds
);
downloadableFile
=
zipFile
.
getRawFile
();
}
else
if
(
downloadKind
.
equals
(
ServerLayout
.
DOWNLOAD_ALL_PROJECTS_SOURCE
))
{
// Download all project source files as a zip of zips.
ProjectSourceZip
zipFile
=
fileExporter
.
exportAllProjectsSourceZip
(
...
...
appinventor/appengine/src/com/google/appinventor/server/FileExporter.java
View file @
127c9611
...
...
@@ -10,6 +10,7 @@ import com.google.appinventor.shared.rpc.project.ProjectSourceZip;
import
com.google.appinventor.shared.rpc.project.RawFile
;
import
java.io.IOException
;
import
java.util.List
;
import
javax.annotation.Nullable
;
...
...
@@ -57,6 +58,19 @@ public interface FileExporter {
boolean
includeScreenShots
,
boolean
fatalError
,
boolean
forGallery
)
throws
IOException
;
/**
* Exports projects selected by the user as a zip of zips.
*
* @param userId the userId
* @param zipName the desired name for the zip
* @param projectIds the list of project ids corresponding to selected projects
* @return the name, contents, and number of files in the zip
* @throws IllegalArgumentException if download request cannot be fulfilled
* (no projects)
* @throws IOException if files cannot be written
*/
ProjectSourceZip
exportSelectedProjectsSourceZip
(
String
userId
,
String
zipName
,
List
<
Long
>
projectIds
)
throws
IOException
;
/**
* Exports all of the user's projects' source files as a zip of zips.
*
...
...
appinventor/appengine/src/com/google/appinventor/server/FileExporterImpl.java
View file @
127c9611
...
...
@@ -76,6 +76,73 @@ public final class FileExporterImpl implements FileExporter {
}
}
@Override
public
ProjectSourceZip
exportSelectedProjectsSourceZip
(
String
userId
,
String
zipName
,
List
<
Long
>
projectIds
)
throws
IOException
{
// Create a zip file for each project's sources.
if
(
projectIds
.
size
()
==
0
)
{
throw
new
IllegalArgumentException
(
"No projects to download"
);
}
ByteArrayOutputStream
zipFile
=
new
ByteArrayOutputStream
();
ZipOutputStream
out
=
new
ZipOutputStream
(
zipFile
);
int
count
=
0
;
String
metadata
=
""
;
for
(
Long
projectId
:
projectIds
)
{
try
{
ProjectSourceZip
projectSourceZip
=
exportProjectSourceZip
(
userId
,
projectId
,
false
,
false
,
null
,
false
,
false
,
false
,
false
);
byte
[]
data
=
projectSourceZip
.
getContent
();
String
name
=
projectSourceZip
.
getFileName
();
// If necessary, renae duplicate projects
while
(
true
)
{
try
{
out
.
putNextEntry
(
new
ZipEntry
(
name
));
break
;
}
catch
(
IOException
e
)
{
name
=
"duplicate-"
+
name
;
}
}
metadata
+=
projectSourceZip
.
getMetadata
()
+
"\n"
;
out
.
write
(
data
,
0
,
data
.
length
);
out
.
closeEntry
();
count
++;
}
catch
(
IllegalArgumentException
e
)
{
System
.
err
.
println
(
"No files found for userid: "
+
userId
+
" for projectid: "
+
projectId
);
}
catch
(
IOException
e
)
{
System
.
err
.
println
(
"IOException while reading files found for userid: "
+
userId
+
" for projectid: "
+
projectId
);
continue
;
}
}
if
(
count
==
0
)
{
throw
new
IllegalArgumentException
(
"No files to download"
);
}
List
<
String
>
userFiles
=
storageIo
.
getUserFiles
(
userId
);
if
(
userFiles
.
contains
(
StorageUtil
.
ANDROID_KEYSTORE_FILENAME
))
{
byte
[]
androidKeystoreBytes
=
storageIo
.
downloadRawUserFile
(
userId
,
StorageUtil
.
ANDROID_KEYSTORE_FILENAME
);
if
(
androidKeystoreBytes
.
length
>
0
)
{
out
.
putNextEntry
(
new
ZipEntry
(
StorageUtil
.
ANDROID_KEYSTORE_FILENAME
));
out
.
write
(
androidKeystoreBytes
,
0
,
androidKeystoreBytes
.
length
);
out
.
closeEntry
();
count
++;
}
}
out
.
close
();
// Package the big zip file up as a ProjectSourceZip and return it.
byte
[]
content
=
zipFile
.
toByteArray
();
ProjectSourceZip
projectSourceZip
=
new
ProjectSourceZip
(
zipName
,
content
,
count
);
projectSourceZip
.
setMetadata
(
metadata
);
return
projectSourceZip
;
}
@Override
public
ProjectSourceZip
exportAllProjectsSourceZip
(
String
userId
,
String
zipName
)
throws
IOException
{
...
...
appinventor/appengine/src/com/google/appinventor/shared/rpc/ServerLayout.java
View file @
127c9611
...
...
@@ -101,6 +101,12 @@ public class ServerLayout {
*/
public
static
final
String
DOWNLOAD_PROJECT_SOURCE
=
"project-source"
;
/**
* Relative path within {@link com.google.appinventor.server.DownloadServlet}
* for downloading selected of a user's projects' sources.
*/
public
static
final
String
DOWNLOAD_SELECTED_PROJECTS_SOURCE
=
"selected-projects-source"
;
/**
* Relative path within {@link com.google.appinventor.server.DownloadServlet}
* for downloading all of a user's projects' sources.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment