Improved: Open FTL File from browser (OFBIZ-12018)
Add function to open the corresponding FTL file with IDE
when the named border is clicked from browser.
Thanks Rishi and Jacques for review
diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java
index 5265304..4ca2e17 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilHtml.java
@@ -125,11 +125,13 @@
String pluginPathKey = File.separator + "plugins" + File.separator;
for (File xmlTheme : xmlThemes) {
String path = xmlTheme.toURI().toURL().toString();
+ // get the path after themes or plugins folders
if (path.indexOf(themePathKey) > 0) {
path = path.substring(path.indexOf(themePathKey) + 8);
} else if (path.indexOf(pluginPathKey) > 0) {
path = path.substring(path.indexOf(pluginPathKey) + 9);
}
+ // get folder name
path = path.substring(0, path.indexOf(File.separator));
if (!path.contains("common-theme") && !path.contains("ecommerce")) {
visualThemeBasePathsName.add(File.separator + path + File.separator);
diff --git a/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java b/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java
index 3ecf80b..6313fbf 100644
--- a/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java
+++ b/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java
@@ -24,9 +24,12 @@
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
+import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -42,17 +45,20 @@
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ofbiz.base.lang.JSON;
+import org.apache.ofbiz.base.location.FlexibleLocation;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilGenerics;
import org.apache.ofbiz.base.util.UtilHttp;
import org.apache.ofbiz.base.util.UtilProperties;
import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.base.util.string.FlexibleStringExpander;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.util.EntityUtilProperties;
import org.apache.ofbiz.webapp.control.JWTManager;
import org.apache.ofbiz.webapp.control.LoginWorker;
+import org.apache.ofbiz.widget.model.ModelWidget;
import org.apache.ofbiz.widget.model.MultiBlockHtmlTemplateUtil;
import org.apache.ofbiz.widget.model.ThemeFactory;
import org.apache.ofbiz.widget.renderer.VisualTheme;
@@ -444,4 +450,36 @@
return "success";
}
+ public static String openSourceFile(HttpServletRequest request, HttpServletResponse response) {
+ ModelWidget.NamedBorderType namedBorderType = ModelWidget.widgetNamedBorderEnabled();
+ if (namedBorderType == ModelWidget.NamedBorderType.SOURCE) {
+ String sourceLocation = request.getParameter("sourceLocation");
+ if (UtilValidate.isNotEmpty(sourceLocation) && sourceLocation.startsWith("component:")) {
+ try {
+ // find absolute path of file
+ URL sourceFileUrl = FlexibleLocation.resolveLocation(sourceLocation);
+ String location = sourceFileUrl.getFile();
+ // prepare content map for string expansion
+ Map<String, Object> sourceMap = new HashMap<>();
+ sourceMap.put("sourceLocation", location);
+ // get command to run
+ String cmdTemplate = UtilProperties.getPropertyValue("widget", "widget.dev.cmd.openSourceFile");
+ String cmd = (String) FlexibleStringExpander.getInstance(cmdTemplate).expand(sourceMap);
+ // run command
+ Process process = Runtime.getRuntime().exec(String.format(cmd, location));
+ // print result
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line = "";
+ while ((line = reader.readLine()) != null) {
+ Debug.logInfo(line, MODULE);
+ }
+ return "success";
+ } catch (IOException e) {
+ Debug.logError(e, MODULE);
+ }
+ }
+ }
+ return "error";
+ }
+
}
diff --git a/framework/common/webcommon/WEB-INF/common-controller.xml b/framework/common/webcommon/WEB-INF/common-controller.xml
index a2dd12a..36b10c9 100644
--- a/framework/common/webcommon/WEB-INF/common-controller.xml
+++ b/framework/common/webcommon/WEB-INF/common-controller.xml
@@ -336,6 +336,13 @@
<response name="error" type="request" value="json"/>
</request-map>
+ <request-map uri="openSourceFile">
+ <security https="false" auth="false"/>
+ <event type="java" path="org.apache.ofbiz.common.CommonEvents" invoke="openSourceFile"/>
+ <response name="success" type="none" />
+ <response name="error" type="none" />
+ </request-map>
+
<!--========================== AJAX events =====================-->
<!-- View Mappings -->
diff --git a/framework/widget/config/widget.properties b/framework/widget/config/widget.properties
index 306fb31..8d7a446 100644
--- a/framework/widget/config/widget.properties
+++ b/framework/widget/config/widget.properties
@@ -28,7 +28,19 @@
widget.verbose=true
# Enable widget named border for development
-widget.dev.namedBorder=true
+# NONE - For production where no named border will be shown.
+# LABEL - Show named border
+# SOURCE - Show named border with link to open the source code
+widget.dev.namedBorder=NONE
+
+# Command template to open file with editor
+# Linux:
+# idea ${sourceLocation}
+# eclipse --launcher.openFile ${sourceLocation}
+# Windows:
+# idea.exe ${sourceLocation}
+# eclipse.exe --launcher.openFile ${sourceLocation}
+widget.dev.cmd.openSourceFile=idea ${sourceLocation}
# Default number of items to be displayed per page in a list form
widget.form.defaultViewSize=20
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
index 3cd8e5f..c75c06f 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/HtmlWidget.java
@@ -167,9 +167,16 @@
if (insertWidgetBoundaryComments) {
writer.append(HtmlWidgetRenderer.buildBoundaryComment("Begin", "Template", location));
}
- boolean insertWidgetNamedBorder = !location.endsWith(".fo.ftl") && ModelWidget.widgetNamedBorderEnabled();
+ boolean insertWidgetNamedBorder = false;
+ NamedBorderType namedBorderType = null;
+ if (!location.endsWith(".fo.ftl")) {
+ namedBorderType = ModelWidget.widgetNamedBorderEnabled();
+ if (namedBorderType != NamedBorderType.NONE) {
+ insertWidgetNamedBorder = true;
+ }
+ }
if (insertWidgetNamedBorder) {
- writer.append(HtmlWidgetRenderer.buildNamedBorder("Begin", "Template", location));
+ writer.append(HtmlWidgetRenderer.buildNamedBorder("Begin", "Template", location, namedBorderType));
}
Template template = null;
@@ -181,7 +188,7 @@
FreeMarkerWorker.renderTemplate(template, context, writer);
if (insertWidgetNamedBorder) {
- writer.append(HtmlWidgetRenderer.buildNamedBorder("End", "Template", location));
+ writer.append(HtmlWidgetRenderer.buildNamedBorder("End", "Template", location, namedBorderType));
}
if (insertWidgetBoundaryComments) {
writer.append(HtmlWidgetRenderer.buildBoundaryComment("End", "Template", location));
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelWidget.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelWidget.java
index 8630fd1..9393d7a 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelWidget.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelWidget.java
@@ -39,6 +39,7 @@
* set to "widgetVerbose".
*/
public static final String ENABLE_BOUNDARY_COMMENTS_PARAM = "widgetVerbose";
+ public enum NamedBorderType { NONE, LABEL, SOURCE }
private final String name;
private final String systemId;
@@ -152,10 +153,10 @@
}
/**
- * Returns <code>true</code> if showing filename and border on the rendered part of the template.
- * @return true if <code>widget.dev.namedBorder</code> is set to <code>true</code>
+ * determine how to display named border for development
+ * @return NamedBorderType from <code>widget.dev.namedBorder</code> property
*/
- public static boolean widgetNamedBorderEnabled() {
- return "true".equals(UtilProperties.getPropertyValue("widget", "widget.dev.namedBorder"));
+ public static NamedBorderType widgetNamedBorderEnabled() {
+ return NamedBorderType.valueOf(UtilProperties.getPropertyValue("widget", "widget.dev.namedBorder", NamedBorderType.NONE.toString()));
}
}
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/html/HtmlWidgetRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/html/HtmlWidgetRenderer.java
index 0b89029..e79a718 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/html/HtmlWidgetRenderer.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/html/HtmlWidgetRenderer.java
@@ -75,16 +75,26 @@
return "<!-- " + boundaryType + " " + widgetType + " " + widgetName + " -->" + WHITE_SPACE;
}
- public static String buildNamedBorder(String boundaryType, String widgetType, String widgetName) {
+ public static String buildNamedBorder(String boundaryType, String widgetType, String widgetName, ModelWidget.NamedBorderType namedBorderType) {
List<String> themeBasePathsToExempt = UtilHtml.getVisualThemeFolderNamesToExempt();
if (!themeBasePathsToExempt.stream().anyMatch(widgetName::contains)) {
// add additional visual label for non-theme ftl
switch (boundaryType) {
case "End":
String fileName = widgetName.substring(widgetName.lastIndexOf(File.separator) + 1);
- return "</div><div class='info-overlay'><span class='info-overlay-item'>"
- + fileName
- + "</span></div></div>";
+ switch (namedBorderType) {
+ case SOURCE:
+ return "</div><div class='info-overlay'><span class='info-overlay-item'><a href='#' data-source='"
+ + widgetName
+ + "'>"
+ + fileName
+ + "</a></span></div></div>";
+ case LABEL:
+ return "</div><div class='info-overlay'><span class='info-overlay-item'>"
+ + fileName
+ + "</span></div></div>";
+ default: return "";
+ }
default:
return "<div class='info-container'><div class='info-content'>";
}
diff --git a/themes/common-theme/webapp/common/js/util/OfbizUtil.js b/themes/common-theme/webapp/common/js/util/OfbizUtil.js
index c59fc35..11b4ab3 100644
--- a/themes/common-theme/webapp/common/js/util/OfbizUtil.js
+++ b/themes/common-theme/webapp/common/js/util/OfbizUtil.js
@@ -44,14 +44,28 @@
bindObservers("body");
function initNamedBorder() {
+ jQuery("[data-source]").off();
// fadeout info-overlay labels
setTimeout(function(){
$('.info-overlay').fadeOut(1000, function(){
+ jQuery("[data-source]").off();
$('.info-container').contents().unwrap();
$('.info-content').contents().unwrap();
$('.info-overlay').delay(1000).remove();
});
}, 3000);
+ // clickable link in named border to open source file
+ jQuery("[data-source]").click(function(){
+ var sourceLocaton = jQuery(this).data("source");
+ jQuery.ajax({
+ url: 'openSourceFile',
+ type: "POST",
+ data: {sourceLocation:sourceLocaton},
+ success: function(data) {
+ alert("Command is sent to open source file with your IDE");
+ }
+ });
+ });
}
initNamedBorder();
});