Unverified Commit 713ff82b authored by Vishwas Adiga's avatar Vishwas Adiga Committed by GitHub

Add ability to include license files in extensions (#2326)

parent 33324c8a
......@@ -1037,6 +1037,10 @@ public interface OdeMessages extends Messages, AutogeneratedOdeMessages {
@Description("Label of the link to a component's reference docs")
String moreInformation();
@DefaultMessage("View license")
@Description("Label of the link to a component's attribution license")
String viewLicense();
// Used in editor/youngandroid/YaFormEditor.java and YaBlocksEditor.java
@DefaultMessage("Server error: could not load file. Please try again later!")
......
......@@ -255,6 +255,15 @@ class ComponentDatabase implements ComponentDatabaseInterface {
return component.getIconName();
}
@Override
public String getLicenseName(String componentName) {
ComponentDefinition component = components.get(componentName);
if (component == null) {
throw new ComponentNotFoundException(componentName);
}
return component.getLicenseName();
}
@Override
public List<PropertyDefinition> getPropertyDefinitions(String componentName) {
ComponentDefinition component = components.get(componentName);
......@@ -341,7 +350,9 @@ class ComponentDatabase implements ComponentDatabaseInterface {
properties.containsKey("helpUrl") ? properties.get("helpUrl").asString().getString() : "",
Boolean.valueOf(properties.get("showOnPalette").asString().getString()),
Boolean.valueOf(properties.get("nonVisible").asString().getString()),
properties.get("iconName").asString().getString(), componentNode.toJson());
properties.get("iconName").asString().getString(),
properties.containsKey("licenseName") ? properties.get("licenseName").asString().getString() : "",
componentNode.toJson());
findComponentProperties(component, properties.get("properties").asArray());
findComponentBlockProperties(component, properties.get("blockProperties").asArray());
findComponentEvents(component, properties.get("events").asArray());
......
......@@ -108,6 +108,13 @@ public final class ComponentHelpWidget extends AbstractPaletteItemWidget {
link.setStyleName("ode-ComponentHelpPopup-Link");
inner.add(link);
}
if (scd.getExternal() && !"".equals(scd.getLicense())) {
String license = scd.getLicense();
HTML viewLicenseHTML = new HTML("<a href=\"" + license + "\" target=\"_blank\">" +
MESSAGES.viewLicense() + "</a>");
viewLicenseHTML.setStyleName("ode-ComponentHelpPopup-Link");
inner.add(viewLicenseHTML);
}
setWidget(inner);
......
......@@ -318,6 +318,18 @@ public final class SimpleComponentDescriptor {
return dateBuilt;
}
/**
* Returns the path to the license file used by the component.
*
* @return path to license file of component
*/
public String getLicense() {
String type = COMPONENT_DATABASE.getComponentType(name);
return getLicenseURLFromPath(COMPONENT_DATABASE.getLicenseName(name),
type.substring(0, type.lastIndexOf('.')),
editor.getProjectId());
}
/**
* Returns a draggable image for the component. Used when dragging a
* component from the palette onto the form.
......@@ -370,6 +382,20 @@ public final class SimpleComponentDescriptor {
}
}
public static String getLicenseURLFromPath(String licensePath, String packageName, long projectId) {
if (licensePath.startsWith("aiwebres/") && packageName != null) {
// License file is inside aiwebres
return StorageUtil.getFileUrl(projectId,
"assets/external_comps/" + packageName + "/" + licensePath) + "&inline";
} else if(licensePath.startsWith("http:") || licensePath.startsWith("https:")) {
// The license is an external URL
return licensePath;
} else {
// No license file specified
return "";
}
}
/**
* Instantiates mock component by name.
*/
......
......@@ -244,7 +244,9 @@ public class DownloadServlet extends OdeServlet {
// Set http response information
resp.setStatus(HttpServletResponse.SC_OK);
resp.setHeader("content-disposition", "attachment; filename=\"" + fileName + "\"");
resp.setHeader(
"content-disposition",
req.getParameter("inline") != null ? "inline" : "attachment" + "; filename=\"" + fileName + "\"");
resp.setContentType(StorageUtil.getContentTypeForFilePath(fileName));
resp.setContentLength(content.length);
......
......@@ -43,11 +43,13 @@ public interface ComponentDatabaseInterface {
private final Map<String, String> propertiesTypesByName;
private final boolean nonVisible;
private final String iconName;
private final String licenseName;
private final String typeDescription;
public ComponentDefinition(String name, int version, String versionName, String dateBuilt, String type, boolean external,
String categoryString, String helpString, String helpUrl,
boolean showOnPalette, boolean nonVisible, String iconName, String typeDescription) {
boolean showOnPalette, boolean nonVisible, String iconName,
String licenseName, String typeDescription) {
this.name = name;
this.version = version;
this.versionName = versionName;
......@@ -66,6 +68,7 @@ public interface ComponentDatabaseInterface {
this.propertiesTypesByName = new HashMap<String, String>();
this.nonVisible = nonVisible;
this.iconName = iconName;
this.licenseName = licenseName;
this.typeDescription = typeDescription;
}
......@@ -156,6 +159,10 @@ public interface ComponentDatabaseInterface {
return iconName;
}
public String getLicenseName() {
return licenseName;
}
public String getTypeDescription() {
return typeDescription;
}
......@@ -461,6 +468,12 @@ public interface ComponentDatabaseInterface {
*/
String getIconName(String componentName);
/**
* Returns the name of the license file used by the component. Intended for use
* by external components.
*/
String getLicenseName(String componentName);
/**
* Returns a list of a component's property definitions.
*
......
......@@ -102,4 +102,14 @@ public @interface DesignerComponent {
* the component.
*/
String dateBuilt() default "";
/**
* The file name of the LICENSE file that the component is attributed under.
* Meant primarily for use by external components which can have a license
* different from that of this codebase. This string can also be a URL pointing
* to an external LICENSE file.
*
* @return The name of the LICENSE file
*/
String licenseName() default "";
}
......@@ -35,6 +35,7 @@ import javax.tools.FileObject;
* "showOnPalette": "true"|"false",
* "nonVisible": "true"|"false",
* "iconName": "ICON-FILE-NAME",
* "licenseName": "LICENSE-FILE-NAME",
* "androidMinSdk": "ANDROID-MIN-SDK",
* "conditionals": {
* "permissions": {
......@@ -109,6 +110,8 @@ public final class ComponentDescriptorGenerator extends ComponentProcessor {
sb.append(component.getNonVisible());
sb.append("\",\n \"iconName\": \"");
sb.append(component.getIconName());
sb.append("\",\n \"licenseName\": \"");
sb.append(component.getLicenseName());
sb.append("\",\n \"androidMinSdk\": ");
sb.append(component.getAndroidMinSdk());
outputConditionalAnnotations(component, sb);
......@@ -213,8 +216,8 @@ public final class ComponentDescriptorGenerator extends ComponentProcessor {
*/
private void outputConditionalAnnotations(ComponentInfo component, StringBuilder sb) {
if (component.conditionalPermissions.size() +
component.conditionalBroadcastReceivers.size() +
component.conditionalServices.size() +
component.conditionalBroadcastReceivers.size() +
component.conditionalServices.size() +
component.conditionalContentProviders.size() == 0) {
return;
}
......
......@@ -717,7 +717,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
* Content providers required by this component.
*/
protected final Set<String> contentProviders;
/**
* TODO(Will): Remove the following field once the deprecated {@link SimpleBroadcastReceiver}
* annotation is removed. It should should remain for the time being
......@@ -780,6 +780,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
private int androidMinSdk;
private String versionName;
private String dateBuilt;
private String licenseName;
protected ComponentInfo(Element element) {
super(element.getSimpleName().toString(), // Short name
......@@ -869,6 +870,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
showOnPalette = designerComponentAnnotation.showOnPalette();
nonVisible = designerComponentAnnotation.nonVisible();
iconName = designerComponentAnnotation.iconName();
licenseName = designerComponentAnnotation.licenseName();
androidMinSdk = designerComponentAnnotation.androidMinSdk();
versionName = designerComponentAnnotation.versionName();
userVisible = designerComponentAnnotation.showOnPalette();
......@@ -989,6 +991,16 @@ public abstract class ComponentProcessor extends AbstractProcessor {
return dateBuilt;
}
/**
* Returns the name of the license file used by external components
* {@link DesignerComponent#licenseName()}.
*
* @return the name of the license file
*/
protected String getLicenseName() {
return licenseName;
}
private String getDisplayNameForComponentType(String componentTypeName) {
// Users don't know what a 'Form' is. They know it as a 'Screen'.
return "Form".equals(componentTypeName) ? "Screen" : componentTypeName;
......@@ -1328,7 +1340,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
throw new RuntimeException(e);
}
}
// TODO(Will): Remove the following legacy code once the deprecated
// @SimpleBroadcastReceiver annotation is removed. It should
// should remain for the time being because otherwise we'll break
......@@ -1338,7 +1350,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
// has a Class Name and zero or more Filter Actions. In the
// resulting String, Class name will go first, and each Action
// will be added, separated by a comma.
SimpleBroadcastReceiver simpleBroadcastReceiver = element.getAnnotation(SimpleBroadcastReceiver.class);
if (simpleBroadcastReceiver != null) {
for (String className : simpleBroadcastReceiver.className().split(",")){
......@@ -1555,7 +1567,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
StringBuilder elementString = new StringBuilder(" <intent-filter ");
elementString.append(elementAttributesToString(element));
elementString.append(">\\n");
// Now, we collect any <intent-filter> subelements.
elementString.append(subelementsToString(element.actionElements()));
elementString.append(subelementsToString(element.categoryElements()));
......@@ -1657,7 +1669,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
}
return attributeString.toString();
}
// Build the subelement String for a given array of XML elements modeled by
// corresponding annotations.
private static String subelementsToString(Annotation[] subelements)
......
......@@ -106,6 +106,7 @@ public class ExternalComponentGenerator {
generateExternalComponentDescriptors(name, entry.getValue());
for (ExternalComponentInfo info : entry.getValue()) {
copyIcon(name, info.descriptor);
copyLicense(name, info.descriptor);
copyAssets(name, info.descriptor);
}
generateExternalComponentBuildFiles(name, entry.getValue());
......@@ -230,6 +231,26 @@ public class ExternalComponentGenerator {
}
}
private static void copyLicense(String packageName, JSONObject componentDescriptor)
throws IOException, JSONException {
String license = componentDescriptor.getString("licenseName");
if("".equals(license) || license.startsWith("http:") || license.startsWith("https:")) {
// License will be loaded from the web
return;
}
String packagePath = packageName.replace('.', File.separatorChar);
File sourceDir = new File(externalComponentsDirPath + File.separator + ".." + File.separator + ".." + File.separator + "src" + File.separator + packagePath);
File licenseFile = new File(sourceDir, license);
if(licenseFile.exists()) {
File destinationLicense = new File(externalComponentsDirPath + File.separator + packageName + File.separator + license);
ensureDirectory(destinationLicense.getParent(), "Unable to create directory " + destinationLicense.getParent());
System.out.println("Extensions : " + "Copying file " + licenseFile.getAbsolutePath());
copyFile(licenseFile.getAbsolutePath(), destinationLicense.getAbsolutePath());
} else {
System.out.println("Extensions : Skipping missing license " + license);
}
}
private static void copyAssets(String packageName, JSONObject componentDescriptor)
throws IOException, JSONException {
JSONArray assets = componentDescriptor.optJSONArray("assets");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment