IDEMPIERE-4421 Html asset versioning should allow fluent deployment (#420)
* IDEMPIERE-4421 Html asset versioning should allow fluent deployment use classpath loading and lang-addon versioning for theme resources * IDEMPIERE-4421 Html asset versioning should allow fluent deployment Incorporate backward compatibility patch from Carlos.
|
@ -51,4 +51,8 @@ Copyright (C) 2007 Ashley G Ramdass (ADempiere WebUI).
|
||||||
<javascript-module name="org.idempiere.commons" version="202011230530"/>
|
<javascript-module name="org.idempiere.commons" version="202011230530"/>
|
||||||
<javascript-module name="jquery.maskedinput" version="1.4.1" />
|
<javascript-module name="jquery.maskedinput" version="1.4.1" />
|
||||||
<javascript-module name="photobooth" version="0.7-rsd3" />
|
<javascript-module name="photobooth" version="0.7-rsd3" />
|
||||||
|
|
||||||
|
<!-- this js module doesn't actually exists and it is here for default theme version -->
|
||||||
|
<!-- since loading of js module is on demand, it doesn't cause any error as long as you don't try to load it -->
|
||||||
|
<javascript-module name="idempiere.theme.default" version="202011282132" />
|
||||||
</language>
|
</language>
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.adempiere.webui.adwindow.ADWindow;
|
||||||
import org.adempiere.webui.desktop.FavouriteController;
|
import org.adempiere.webui.desktop.FavouriteController;
|
||||||
import org.adempiere.webui.exception.ApplicationException;
|
import org.adempiere.webui.exception.ApplicationException;
|
||||||
import org.adempiere.webui.session.SessionManager;
|
import org.adempiere.webui.session.SessionManager;
|
||||||
import org.adempiere.webui.theme.ITheme;
|
|
||||||
import org.adempiere.webui.theme.ThemeManager;
|
import org.adempiere.webui.theme.ThemeManager;
|
||||||
import org.adempiere.webui.window.FDialog;
|
import org.adempiere.webui.window.FDialog;
|
||||||
import org.compiere.model.MMenu;
|
import org.compiere.model.MMenu;
|
||||||
|
@ -175,7 +174,7 @@ public class DPFavourites extends DashboardPanel implements EventListener<Event>
|
||||||
btnFavItem.setTooltiptext(description);
|
btnFavItem.setTooltiptext(description);
|
||||||
if (ThemeManager.isUseFontIconForImage())
|
if (ThemeManager.isUseFontIconForImage())
|
||||||
btnFavItem.setIconSclass(imageSrc);
|
btnFavItem.setIconSclass(imageSrc);
|
||||||
else if (imageSrc.startsWith(ITheme.THEME_PATH_PREFIX))
|
else if (imageSrc.startsWith(ThemeManager.THEME_PATH_PREFIX))
|
||||||
btnFavItem.setImage(imageSrc);
|
btnFavItem.setImage(imageSrc);
|
||||||
else
|
else
|
||||||
btnFavItem.setImage(ThemeManager.getThemeResource(imageSrc));
|
btnFavItem.setImage(ThemeManager.getThemeResource(imageSrc));
|
||||||
|
|
|
@ -21,8 +21,9 @@ package org.adempiere.webui.theme;
|
||||||
public interface ITheme {
|
public interface ITheme {
|
||||||
//default theme
|
//default theme
|
||||||
public static final String ZK_THEME_DEFAULT = "default";
|
public static final String ZK_THEME_DEFAULT = "default";
|
||||||
//theme resource url prefix
|
//theme resource url prefix. ~./ is the zk url prefix for resources loaded from classpath (typically at src/web folder)
|
||||||
public static final String THEME_PATH_PREFIX = "/theme/";
|
public static final String THEME_PATH_PREFIX_V8 = "~./theme/";
|
||||||
|
public static final String THEME_PATH_PREFIX_V7 = "/theme/"; // for backward compatibility
|
||||||
|
|
||||||
//css for login window and box
|
//css for login window and box
|
||||||
public static final String LOGIN_WINDOW_CLASS = "login-window";
|
public static final String LOGIN_WINDOW_CLASS = "login-window";
|
||||||
|
|
|
@ -32,18 +32,26 @@ import org.zkoss.image.AImage;
|
||||||
*/
|
*/
|
||||||
public final class ThemeManager {
|
public final class ThemeManager {
|
||||||
|
|
||||||
|
//zk predefined starting path for classpath resources (src/web)
|
||||||
|
public static final String ZK_PREFIX_FOR_CLASSPATH_RESOURCE = "/web";
|
||||||
|
|
||||||
|
//zk predefined url prefix for resources loaded from classpath
|
||||||
|
public static final String ZK_URL_PREFIX_FOR_CLASSPATH_RESOURCE = "~./";
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
private static CLogger log = CLogger.getCLogger(ThemeManager.class);
|
private static CLogger log = CLogger.getCLogger(ThemeManager.class);
|
||||||
|
|
||||||
private static String m_theme = null;
|
private static String m_theme = ITheme.ZK_THEME_DEFAULT;
|
||||||
private static String m_brokenTheme = null;
|
private static String m_brokenTheme = null;
|
||||||
|
|
||||||
|
public static String THEME_PATH_PREFIX = ITheme.THEME_PATH_PREFIX_V8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return url for large logo
|
* @return url for large logo
|
||||||
*/
|
*/
|
||||||
public static String getLargeLogo() {
|
public static String getLargeLogo() {
|
||||||
String theme = getTheme();
|
String theme = getTheme();
|
||||||
String def = ITheme.THEME_PATH_PREFIX+theme+ITheme.LOGIN_LOGO_IMAGE;
|
String def = THEME_PATH_PREFIX+theme+ITheme.LOGIN_LOGO_IMAGE;
|
||||||
return MSysConfig.getValue(MSysConfig.ZK_LOGO_LARGE, def);
|
return MSysConfig.getValue(MSysConfig.ZK_LOGO_LARGE, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +60,7 @@ public final class ThemeManager {
|
||||||
*/
|
*/
|
||||||
public static String getSmallLogo() {
|
public static String getSmallLogo() {
|
||||||
String theme = getTheme();
|
String theme = getTheme();
|
||||||
String def = ITheme.THEME_PATH_PREFIX+theme+ITheme.HEADER_LOGO_IMAGE;
|
String def = THEME_PATH_PREFIX+theme+ITheme.HEADER_LOGO_IMAGE;
|
||||||
String url = MSysConfig.getValue(MSysConfig.ZK_LOGO_SMALL, null);
|
String url = MSysConfig.getValue(MSysConfig.ZK_LOGO_SMALL, null);
|
||||||
if (url == null)
|
if (url == null)
|
||||||
url = MSysConfig.getValue(MSysConfig.WEBUI_LOGOURL, def);
|
url = MSysConfig.getValue(MSysConfig.WEBUI_LOGOURL, def);
|
||||||
|
@ -72,11 +80,21 @@ public final class ThemeManager {
|
||||||
if (! theme.equals(m_theme)) {
|
if (! theme.equals(m_theme)) {
|
||||||
if (! ITheme.ZK_THEME_DEFAULT.equals(theme)) {
|
if (! ITheme.ZK_THEME_DEFAULT.equals(theme)) {
|
||||||
// Verify the theme.css.dsp exists in the theme folder
|
// Verify the theme.css.dsp exists in the theme folder
|
||||||
if (ThemeManager.class.getResource(ITheme.THEME_PATH_PREFIX + theme + ITheme.THEME_STYLESHEET) == null) {
|
String themeCSSURL = THEME_PATH_PREFIX + theme + ITheme.THEME_STYLESHEET;
|
||||||
log.warning("The theme " + theme + " does not exist or is not properly configured, falling back to default");
|
if (ThemeManager.class.getResource(toClassPathResourcePath(themeCSSURL)) == null) {
|
||||||
m_brokenTheme = theme;
|
// verify if is a v7 theme
|
||||||
theme = ITheme.ZK_THEME_DEFAULT;
|
themeCSSURL = ITheme.THEME_PATH_PREFIX_V7 + theme + ITheme.THEME_STYLESHEET;
|
||||||
|
if (ThemeManager.class.getResource(toClassPathResourcePath(themeCSSURL)) != null) {
|
||||||
|
THEME_PATH_PREFIX = ITheme.THEME_PATH_PREFIX_V7;
|
||||||
|
} else {
|
||||||
|
log.warning("The theme " + theme + " does not exist or is not properly configured, falling back to default");
|
||||||
|
m_brokenTheme = theme;
|
||||||
|
THEME_PATH_PREFIX = ITheme.THEME_PATH_PREFIX_V8;
|
||||||
|
theme = ITheme.ZK_THEME_DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
THEME_PATH_PREFIX = ITheme.THEME_PATH_PREFIX_V8;
|
||||||
}
|
}
|
||||||
m_theme = theme;
|
m_theme = theme;
|
||||||
}
|
}
|
||||||
|
@ -88,21 +106,21 @@ public final class ThemeManager {
|
||||||
* @return url of theme stylesheet
|
* @return url of theme stylesheet
|
||||||
*/
|
*/
|
||||||
public static String getStyleSheet() {
|
public static String getStyleSheet() {
|
||||||
return ITheme.THEME_PATH_PREFIX + getTheme() + ITheme.THEME_STYLESHEET;
|
return THEME_PATH_PREFIX + getTheme() + ITheme.THEME_STYLESHEET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return url of theme stylesheet by browser
|
* @return url of theme stylesheet by browser
|
||||||
*/
|
*/
|
||||||
public static String getStyleSheetByBrowser() {
|
public static String getStyleSheetByBrowser() {
|
||||||
return ITheme.THEME_PATH_PREFIX + getTheme() + ITheme.THEME_STYLESHEET_BY_BROWSER;
|
return THEME_PATH_PREFIX + getTheme() + ITheme.THEME_STYLESHEET_BY_BROWSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return url of theme preference page
|
* @return url of theme preference page
|
||||||
*/
|
*/
|
||||||
public static String getPreference() {
|
public static String getPreference() {
|
||||||
return ITheme.THEME_PATH_PREFIX + getTheme() + ITheme.THEME_PREFERENCE;
|
return THEME_PATH_PREFIX + getTheme() + ITheme.THEME_PREFERENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,7 +135,7 @@ public final class ThemeManager {
|
||||||
*/
|
*/
|
||||||
public static String getBrowserIcon() {
|
public static String getBrowserIcon() {
|
||||||
String theme = getTheme();
|
String theme = getTheme();
|
||||||
String def = ITheme.THEME_PATH_PREFIX + theme + ITheme.BROWSER_ICON_IMAGE;
|
String def = THEME_PATH_PREFIX + theme + ITheme.BROWSER_ICON_IMAGE;
|
||||||
return MSysConfig.getValue(MSysConfig.ZK_BROWSER_ICON, def);
|
return MSysConfig.getValue(MSysConfig.ZK_BROWSER_ICON, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +145,7 @@ public final class ThemeManager {
|
||||||
* @return full resource url
|
* @return full resource url
|
||||||
*/
|
*/
|
||||||
public static String getThemeResource(String name) {
|
public static String getThemeResource(String name) {
|
||||||
StringBuilder builder = new StringBuilder(ITheme.THEME_PATH_PREFIX);
|
StringBuilder builder = new StringBuilder(THEME_PATH_PREFIX);
|
||||||
builder.append(getTheme());
|
builder.append(getTheme());
|
||||||
builder.append("/").append(name);
|
builder.append("/").append(name);
|
||||||
String url = builder.toString().intern();
|
String url = builder.toString().intern();
|
||||||
|
@ -173,7 +191,7 @@ public final class ThemeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final CCache<String, Boolean> s_themeHasCustomCSSCache = new CCache<String, Boolean>(null, "ThemeHasCustomCSSCache", 2, -1, false);
|
private static final CCache<String, Boolean> s_themeHasCustomCSSCache = new CCache<String, Boolean>(null, "ThemeHasCustomCSSCache", 2, -1, false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if custom css exists
|
* @return true if custom css exists
|
||||||
*/
|
*/
|
||||||
|
@ -182,7 +200,8 @@ public final class ThemeManager {
|
||||||
Boolean flag = s_themeHasCustomCSSCache.get(theme);
|
Boolean flag = s_themeHasCustomCSSCache.get(theme);
|
||||||
if (flag != null)
|
if (flag != null)
|
||||||
return flag;
|
return flag;
|
||||||
if (ThemeManager.class.getResource(ITheme.THEME_PATH_PREFIX + theme + "/css/fragment/custom.css.dsp") == null) {
|
String customCSSURL = THEME_PATH_PREFIX + theme + "/css/fragment/custom.css.dsp";
|
||||||
|
if (ThemeManager.class.getResource(toClassPathResourcePath(customCSSURL)) == null) {
|
||||||
flag = Boolean.FALSE;
|
flag = Boolean.FALSE;
|
||||||
} else {
|
} else {
|
||||||
flag = Boolean.TRUE;
|
flag = Boolean.TRUE;
|
||||||
|
@ -197,5 +216,19 @@ public final class ThemeManager {
|
||||||
|
|
||||||
public static boolean isUseFontIconForImage() {
|
public static boolean isUseFontIconForImage() {
|
||||||
return "Y".equals(Env.getContext(Env.getCtx(), ITheme.USE_FONT_ICON_FOR_IMAGE));
|
return "Y".equals(Env.getContext(Env.getCtx(), ITheme.USE_FONT_ICON_FOR_IMAGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param zkResourceURL zk resource url for classpath resources (url start with ~./)
|
||||||
|
* @return Resource path for lookup/loading through class loader (absolute path start with /web)
|
||||||
|
*/
|
||||||
|
public static String toClassPathResourcePath(String zkResourceURL) {
|
||||||
|
if (zkResourceURL == null)
|
||||||
|
return zkResourceURL;
|
||||||
|
|
||||||
|
if (!zkResourceURL.startsWith(ZK_URL_PREFIX_FOR_CLASSPATH_RESOURCE))
|
||||||
|
return zkResourceURL;
|
||||||
|
|
||||||
|
return ZK_PREFIX_FOR_CLASSPATH_RESOURCE+zkResourceURL.substring(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,13 +74,21 @@ public class WebUIResourceFinder implements IResourceFinder {
|
||||||
}
|
}
|
||||||
} else if (url == null && name.startsWith("images/")) {
|
} else if (url == null && name.startsWith("images/")) {
|
||||||
String t = ThemeManager.getThemeResource(name);
|
String t = ThemeManager.getThemeResource(name);
|
||||||
e = find(t);
|
if (t.startsWith(ThemeManager.ZK_URL_PREFIX_FOR_CLASSPATH_RESOURCE)) {
|
||||||
url = e != null && e.hasMoreElements() ? e.nextElement() : null;
|
url = ThemeManager.class.getResource(ThemeManager.toClassPathResourcePath(t));
|
||||||
if (url == null && t.endsWith(".gif")) {
|
} else {
|
||||||
t = t.replace(".gif", ".png");
|
|
||||||
e = find(t);
|
e = find(t);
|
||||||
url = e != null && e.hasMoreElements() ? e.nextElement() : null;
|
url = e != null && e.hasMoreElements() ? e.nextElement() : null;
|
||||||
}
|
}
|
||||||
|
if (url == null && t.endsWith(".gif")) {
|
||||||
|
t = t.replace(".gif", ".png");
|
||||||
|
if (t.startsWith(ThemeManager.ZK_URL_PREFIX_FOR_CLASSPATH_RESOURCE)) {
|
||||||
|
url = ThemeManager.class.getResource(ThemeManager.toClassPathResourcePath(t));
|
||||||
|
} else {
|
||||||
|
e = find(t);
|
||||||
|
url = e != null && e.hasMoreElements() ? e.nextElement() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (url == null && name.endsWith(".gif")) {
|
} else if (url == null && name.endsWith(".gif")) {
|
||||||
String t = name.replace(".gif", ".png");
|
String t = name.replace(".gif", ".png");
|
||||||
e = find(t);
|
e = find(t);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
.z-grid tbody tr.grid-inactive-row td.row-indicator-selected {
|
.z-grid tbody tr.grid-inactive-row td.row-indicator-selected {
|
||||||
background-color: #DCDAD4 !important;
|
background-color: #DCDAD4 !important;
|
||||||
background-image: url(${c:encodeURL('/theme/default/images/EditRecord16.png')}) !important;
|
background-image: url(${c:encodeURL('~./theme/default/images/EditRecord16.png')}) !important;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
.z-grid tbody tr.highlight td.row-indicator-selected {
|
.z-grid tbody tr.highlight td.row-indicator-selected {
|
||||||
background-color: #FFFFCC !important;
|
background-color: #FFFFCC !important;
|
||||||
background-image: url(${c:encodeURL('/theme/default/images/EditRecord16.png')}) !important;
|
background-image: url(${c:encodeURL('~./theme/default/images/EditRecord16.png')}) !important;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
Before Width: | Height: | Size: 588 B After Width: | Height: | Size: 588 B |
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 933 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 503 B |
Before Width: | Height: | Size: 645 B After Width: | Height: | Size: 645 B |
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 466 B |
Before Width: | Height: | Size: 636 B After Width: | Height: | Size: 636 B |
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 622 B |
Before Width: | Height: | Size: 639 B After Width: | Height: | Size: 639 B |
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 808 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 916 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 464 B |
Before Width: | Height: | Size: 678 B After Width: | Height: | Size: 678 B |
Before Width: | Height: | Size: 616 B After Width: | Height: | Size: 616 B |
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 815 B |
Before Width: | Height: | Size: 961 B After Width: | Height: | Size: 961 B |
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 551 B |
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 719 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 546 B After Width: | Height: | Size: 546 B |
Before Width: | Height: | Size: 656 B After Width: | Height: | Size: 656 B |
Before Width: | Height: | Size: 619 B After Width: | Height: | Size: 619 B |
Before Width: | Height: | Size: 843 B After Width: | Height: | Size: 843 B |
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
Before Width: | Height: | Size: 678 B After Width: | Height: | Size: 678 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 773 B After Width: | Height: | Size: 773 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
Before Width: | Height: | Size: 310 B After Width: | Height: | Size: 310 B |
Before Width: | Height: | Size: 737 B After Width: | Height: | Size: 737 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 691 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 814 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 974 B After Width: | Height: | Size: 974 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 803 B |
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 267 B |
Before Width: | Height: | Size: 619 B After Width: | Height: | Size: 619 B |
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 793 B After Width: | Height: | Size: 793 B |
Before Width: | Height: | Size: 913 B After Width: | Height: | Size: 913 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |