IDEMPIERE-5527 Implement asynchronous rendering of dashboard gadget p… (#1621)

* IDEMPIERE-5527 Implement asynchronous rendering of dashboard gadget panel

* IDEMPIERE-5527 Implement asynchronous rendering of dashboard gadget panel

- add DashboardPanel.isLazy
- use background thread

* IDEMPIERE-5527 Implement asynchronous rendering of dashboard gadget panel

- Fix aynsc rendering of dashboard gadget
This commit is contained in:
hengsin 2023-01-17 00:58:37 +08:00 committed by GitHub
parent c047592862
commit a24de47473
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 362 additions and 196 deletions

View File

@ -57,6 +57,6 @@ Copyright (C) 2007 Ashley G Ramdass (ADempiere WebUI).
<!-- 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="202211301708" />
<javascript-module name="idempiere.theme.default" version="202301160600" />
</language>

View File

@ -57,6 +57,16 @@ public class WDocumentStatusIndicator extends Panel implements EventListener<Eve
* @param documentStatus
*/
public WDocumentStatusIndicator(MDocumentStatus documentStatus)
{
this(documentStatus, false);
}
/**
* Constructor
* @param documentStatus
* @param lazy
*/
public WDocumentStatusIndicator(MDocumentStatus documentStatus, boolean lazy)
{
super();
@ -65,8 +75,11 @@ public class WDocumentStatusIndicator extends Panel implements EventListener<Eve
init();
this.setSclass("activities-box");
refresh();
updateUI();
if (!lazy)
{
refresh();
updateUI();
}
} // WDocumentStatusIndicator
private MDocumentStatus m_documentStatus = null;

View File

@ -55,6 +55,8 @@ public class WDocumentStatusPanel extends Panel {
/** Document Status Indicators */
private MDocumentStatus[] m_indicators = null;
private int lastRefreshCount;
/** Logger */
private static final CLogger log = CLogger.getCLogger (WDocumentStatusPanel.class);
@ -102,27 +104,28 @@ public class WDocumentStatusPanel extends Panel {
Row row = new Row();
rows.appendChild(row);
WDocumentStatusIndicator pi = new WDocumentStatusIndicator(m_indicators[i]);
WDocumentStatusIndicator pi = new WDocumentStatusIndicator(m_indicators[i], true);
row.appendChild(pi);
indicatorList.add(pi);
}
} // init
public void refresh() {
int count = 0;
lastRefreshCount = 0;
for (WDocumentStatusIndicator indicator : indicatorList) {
indicator.refresh();
if (indicator.getDocumentStatus().getAD_Client_ID() == 0)
count += indicator.getStatusCount();
lastRefreshCount += indicator.getStatusCount();
}
EventQueue<Event> queue = EventQueues.lookup(IDesktop.ACTIVITIES_EVENT_QUEUE, true);
Event event = new Event(IDesktop.ON_ACTIVITIES_CHANGED_EVENT, null, count);
queue.publish(event);
}
public void updateUI() {
public void updateUI() {
for (WDocumentStatusIndicator indicator : indicatorList) {
indicator.updateUI();
}
EventQueue<Event> queue = EventQueues.lookup(IDesktop.ACTIVITIES_EVENT_QUEUE, true);
Event event = new Event(IDesktop.ON_ACTIVITIES_CHANGED_EVENT, null, lastRefreshCount);
queue.publish(event);
}
}

View File

@ -178,4 +178,9 @@ public class DPActivities extends DashboardPanel implements EventListener<Event>
}
}
}
@Override
public boolean isLazy() {
return true;
}
}

View File

@ -597,4 +597,9 @@ public class DPCalendar extends DashboardPanel implements EventListener<Event>,
trx.removeTrxEventListener(this);
}
}
@Override
public boolean isLazy() {
return true;
}
}

View File

@ -52,7 +52,7 @@ public class DPDocumentStatus extends DashboardPanel implements EventListener<Ev
@Override
public void refresh(ServerPushTemplate template) {
statusPanel.refresh();
template.execute(this);
template.executeAsync(this);
}
@Override
@ -104,4 +104,9 @@ public class DPDocumentStatus extends DashboardPanel implements EventListener<Ev
public boolean isPooling() {
return true;
}
@Override
public boolean isLazy() {
return true;
}
}

View File

@ -13,13 +13,9 @@
*****************************************************************************/
package org.adempiere.webui.dashboard;
import org.adempiere.util.ContextRunnable;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.apps.DesktopRunnable;
import org.adempiere.webui.apps.graph.WPAPanel;
import org.adempiere.webui.apps.graph.WPerformanceIndicator.Options;
import org.adempiere.webui.util.ServerPushTemplate;
import org.compiere.Adempiere;
import org.compiere.model.MGoal;
import org.compiere.model.MSysConfig;
import org.compiere.util.Env;
@ -44,6 +40,7 @@ public class DPPerformance extends DashboardPanel {
private static final long serialVersionUID = -8878665031716441912L;
private WPAPanel paPanel;
private MGoal[] performanceData;
public DPPerformance()
{
@ -53,43 +50,18 @@ public class DPPerformance extends DashboardPanel {
// and can't update when finish load data
paPanel = new WPAPanel();
appendChild(paPanel);
Adempiere.getThreadPoolExecutor().submit(new DesktopRunnable(new LoadPerfomanceData(this), Executions.getCurrent().getDesktop()));
}
static class LoadPerfomanceData extends ContextRunnable{
private DPPerformance parentCtr;
public LoadPerfomanceData (DPPerformance parentCtr){
this.parentCtr = parentCtr;
}
@Override
protected void doRun() {
MGoal [] performanceData = WPAPanel.loadGoal();
AEnv.executeAsyncDesktopTask(new Runnable() {
@Override
public void run() {
if (performanceData != null && performanceData.length > 0){
parentCtr.paPanel.setGoals(performanceData, (Options)null);
if (parentCtr.getAttribute(ON_POST_RENDER_ATTR) == null) {
parentCtr.setAttribute(ON_POST_RENDER_ATTR, Boolean.TRUE);
Events.echoEvent("onPostRender", parentCtr, null);
}
}
}
});
}
}
public void refresh(ServerPushTemplate template) {
super.refresh(template);
performanceData = WPAPanel.loadGoal();
if (Executions.getCurrent() != null) {
updateUI();
if (this.getAttribute(ON_POST_RENDER_ATTR) == null && paPanel.getChildren().size() > 0) {
setAttribute(ON_POST_RENDER_ATTR, Boolean.TRUE);
Events.echoEvent("onPostRender", this, null);
}
} else {
template.executeAsync(this);
}
}
@ -121,4 +93,15 @@ public class DPPerformance extends DashboardPanel {
this.getFirstChild().invalidate();
}
}
@Override
public void updateUI() {
paPanel.setGoals(performanceData, (Options)null);
performanceData = null;
}
@Override
public boolean isLazy() {
return true;
}
}

View File

@ -93,7 +93,6 @@ public class DPRecentItems extends DashboardPanel implements EventListener<Event
ZKUpdateUtil.setHflex(bxRecentItems, "1");
this.setSclass("recentitems-box");
recentItemsContent.appendChild(bxRecentItems);
createRecentItemsPanel();
Toolbar recentItemsToolbar = new Toolbar();
this.appendChild(recentItemsToolbar);
@ -162,11 +161,6 @@ public class DPRecentItems extends DashboardPanel implements EventListener<Event
}
}
private void createRecentItemsPanel()
{
refresh();
}
/**
* Make Recent Item remove persistent
* @param AD_RecentItem_ID Recent Item ID
@ -365,6 +359,11 @@ public class DPRecentItems extends DashboardPanel implements EventListener<Event
cleanup();
}
@Override
public boolean isLazy() {
return true;
}
static class TopicSubscriber implements ITopicSubscriber<Integer> {
@Override
public void onMessage(Integer message) {

View File

@ -82,7 +82,6 @@ public class DPRunningJobs extends DashboardPanel implements EventListener<Event
ZKUpdateUtil.setHflex(bxJobs, "1");
this.setSclass("recentitems-box");
jobsContent.appendChild(bxJobs);
createJobsPanel();
Toolbar jobsToolbar = new Toolbar();
this.appendChild(jobsToolbar);
@ -133,11 +132,6 @@ public class DPRunningJobs extends DashboardPanel implements EventListener<Event
}
}
private void createJobsPanel()
{
refresh();
}
@Override
public void onEvent(Event event) throws Exception
{
@ -281,6 +275,11 @@ public class DPRunningJobs extends DashboardPanel implements EventListener<Event
cleanup();
}
@Override
public boolean isLazy() {
return true;
}
static class TopicSubscriber implements ITopicSubscriber<Integer> {
@Override
public void onMessage(Integer message) {

View File

@ -33,9 +33,11 @@ public abstract class DashboardPanel extends Window implements IDashboardPanel {
super();
}
@Override
public void refresh(ServerPushTemplate template) {
}
@Override
public void updateUI() {
}
@ -45,4 +47,12 @@ public abstract class DashboardPanel extends Window implements IDashboardPanel {
public boolean isPooling() {
return false;
}
/**
* Override this together with refresh and updateUI to implement background loading of gadget
* @return true if panel created without loading of data
*/
public boolean isLazy() {
return false;
}
}

View File

@ -33,10 +33,12 @@ import java.util.Properties;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.adempiere.util.ContextRunnable;
import org.adempiere.webui.ClientInfo;
import org.adempiere.webui.Extensions;
import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.apps.BusyDialog;
import org.adempiere.webui.apps.graph.IChartRendererService;
import org.adempiere.webui.apps.graph.WGraph;
import org.adempiere.webui.apps.graph.WPAWidget;
@ -49,8 +51,10 @@ import org.adempiere.webui.dashboard.DashboardRunnable;
import org.adempiere.webui.report.HTMLExtension;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.ServerPushTemplate;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.adempiere.webui.window.ZkReportViewerProvider;
import org.compiere.Adempiere;
import org.compiere.model.I_AD_Menu;
import org.compiere.model.MChart;
import org.compiere.model.MDashboardContent;
@ -200,7 +204,7 @@ public class DashboardController implements EventListener<Event> {
int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
int AD_Role_ID = Env.getAD_Role_ID(Env.getCtx());
MDashboardPreference[] dps = MDashboardPreference.getForSession(AD_User_ID, AD_Role_ID, true);
MDashboardPreference[] dps = MDashboardPreference.getForSession(AD_User_ID, AD_Role_ID, true);
MDashboardContent [] dcs = MDashboardContentAccess.get(Env.getCtx(), AD_Role_ID, AD_User_ID, null);
if(dps.length == 0){
@ -245,9 +249,9 @@ public class DashboardController implements EventListener<Event> {
{
dashboardColumnLayout = new Vlayout();
dashboardColumnLayout.setSclass("dashboard-column");
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, columnNo);
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, false);
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, columnNo);
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, false);
Anchorchildren dashboardColumn = new Anchorchildren();
dashboardColumn.setAnchor(width + "%" + " 100%");
if (!ClientInfo.isMobile())
@ -269,19 +273,48 @@ public class DashboardController implements EventListener<Event> {
} else {
panel = newGadgetPanel(dp, dc);
}
if (panel != null && panel.getAttribute(PANEL_EMPTY_ATTRIBUTE) == null)
if (panel != null && panel.getAttribute(PANEL_EMPTY_ATTRIBUTE) == null)
dashboardColumnLayout.appendChild(panel);
if (!update) {
renderGadgetPanel(dc, panel);
final Panel fp = panel;
ServerPushTemplate spt = new ServerPushTemplate(dashboardLayout.getDesktop());
IDesktop appDesktop = SessionManager.getAppDesktop();
String contextPath = Executions.getCurrent().getContextPath();
Panelchildren panelChildren = new Panelchildren();
fp.appendChild(panelChildren);
BusyDialog busyDialog = new BusyDialog();
busyDialog.setShadow(false);
panelChildren.appendChild(busyDialog);
//must create zulfile component in foreground UI thread
Component zComponent = null;
if (!Util.isEmpty(dc.getZulFilePath(), true)) {
try {
zComponent = Extensions.getDashboardGadget(dc.getZulFilePath(), panelChildren, dc);
} catch (Exception e) {
throw new AdempiereException(e);
}
}
final Component zulComponent = zComponent;
ContextRunnable cr = new ContextRunnable() {
@Override
protected void doRun() {
try {
asyncRenderGadgetPanel(spt, dc, fp, appDesktop, contextPath, panelChildren, zulComponent);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Adempiere.getThreadPoolExecutor().submit(cr);
}
}
if (dps.length == 0)
{
dashboardColumnLayout = new Vlayout();
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, "0");
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, true);
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, "0");
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, true);
Anchorchildren dashboardColumn = new Anchorchildren();
dashboardColumn.setAnchor((width-5) + "%" + " 100%");
if (!ClientInfo.isMobile())
@ -299,9 +332,9 @@ public class DashboardController implements EventListener<Event> {
// additional column
dashboardColumnLayout = new Vlayout();
ZKUpdateUtil.setWidth(dashboardColumnLayout, "100%");
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, currentColumnNo + 1);
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, true);
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, currentColumnNo + 1);
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, true);
Anchorchildren dashboardColumn = new Anchorchildren();
dashboardColumn.setAnchor(extraWidth + "% 100%");
if (!ClientInfo.isMobile())
@ -321,9 +354,9 @@ public class DashboardController implements EventListener<Event> {
}
//
if (!update && !dashboardRunnable.isEmpty())
if (!update)
{
startDashboardRunnable(parent);
startDashboardRunnable(parent);
}
}
@ -387,19 +420,50 @@ public class DashboardController implements EventListener<Event> {
});
}
private void renderGadgetPanel(MDashboardContent dc, Panel panel) throws Exception {
Panelchildren content = new Panelchildren();
panel.appendChild(content);
boolean panelEmpty = true;
panelEmpty = !render(content, dc, dashboardRunnable);
if (panelEmpty) {
panel.detach();
panel.setAttribute(PANEL_EMPTY_ATTRIBUTE, Boolean.TRUE);
}
/**
* Render gadget panel in background thread
* @param spt
* @param dashboardContent
* @param panel
* @param appDesktop
* @param contextPath
* @param panelChildren
* @param zulComponent
* @throws Exception
*/
private void asyncRenderGadgetPanel(ServerPushTemplate spt, MDashboardContent dashboardContent, Panel panel, IDesktop appDesktop, String contextPath,
Panelchildren panelChildren, Component zulComponent) throws Exception {
List<Component> components = new ArrayList<>();
asyncRenderComponents(dashboardContent, dashboardRunnable, appDesktop, contextPath, panelChildren, components, zulComponent);
if (components.size() > 0) {
for(Component c : components) {
if (c.getParent() != panelChildren) {
spt.executeAsync(() -> panelChildren.appendChild(c));
}
if (c instanceof DashboardPanel) {
DashboardPanel dpanel = (DashboardPanel) c;
if (dpanel.isLazy()) {
try {
dpanel.refresh(spt);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}
}
spt.executeAsync(() -> {
if (panelChildren.getFirstChild() != null && panelChildren.getFirstChild() instanceof BusyDialog)
panelChildren.getFirstChild().detach();
});
} else {
spt.executeAsync(() -> {
panel.detach();
panel.setAttribute(PANEL_EMPTY_ATTRIBUTE, Boolean.TRUE);
});
}
}
private void startDashboardRunnable(Component parent) {
dashboardRunnable.refreshDashboard(false);
// default Update every one minutes
int interval = MSysConfig.getIntValue(MSysConfig.ZK_DASHBOARD_REFRESH_INTERVAL, 60000);
dashboardTimer = new Timer();
@ -442,7 +506,7 @@ public class DashboardController implements EventListener<Event> {
int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
int AD_Role_ID = Env.getAD_Role_ID(Env.getCtx());
MDashboardPreference[] dps = MDashboardPreference.getForSession(AD_User_ID, AD_Role_ID, false);
MDashboardPreference[] dps = MDashboardPreference.getForSession(AD_User_ID, AD_Role_ID, false);
MDashboardContent [] dcs = MDashboardContentAccess.get(Env.getCtx(), AD_Role_ID, AD_User_ID, null);
if(dps.length == 0){
@ -478,12 +542,12 @@ public class DashboardController implements EventListener<Event> {
int lineNo = dp.getLine().intValue();
int flexGrow = (flexGrow = dp.getFlexGrow()) > 0 ? flexGrow : DEFAULT_FLEX_GROW;
if(dashboardLineLayout == null || currentLineNo != lineNo)
if(dashboardLineLayout == null || currentLineNo != lineNo)
{
dashboardLineLayout = new Hlayout();
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, lineNo);
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, false);
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, lineNo);
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, false);
dashboardLineLayout.setSclass("dashboard-row");
Anchorchildren dashboardLine = new Anchorchildren();
dashboardLine.setAnchor(width + "%");
@ -503,24 +567,53 @@ public class DashboardController implements EventListener<Event> {
if (update) {
panel = findPanel(dp.getPA_DashboardContent_ID(), dp.getPA_DashboardPreference_ID());
} else {
panel = newGadgetPanel(dp, dc);
panel.setAttribute(FLEX_GROW_ATTRIBUTE, String.valueOf(flexGrow));
panel = newGadgetPanel(dp, dc);
panel.setAttribute(FLEX_GROW_ATTRIBUTE, String.valueOf(flexGrow));
ZKUpdateUtil.setHflex(panel, String.valueOf(flexGrow));
}
if (panel != null && panel.getAttribute(PANEL_EMPTY_ATTRIBUTE) == null) {
if (panel != null && panel.getAttribute(PANEL_EMPTY_ATTRIBUTE) == null) {
dashboardLineLayout.appendChild(panel);
}
if (!update) {
renderGadgetPanel(dc, panel);
final Panel fp = panel;
ServerPushTemplate spt = new ServerPushTemplate(dashboardLayout.getDesktop());
IDesktop appDesktop = SessionManager.getAppDesktop();
String contextPath = Executions.getCurrent().getContextPath();
Panelchildren panelChildren = new Panelchildren();
fp.appendChild(panelChildren);
BusyDialog busyDialog = new BusyDialog();
busyDialog.setShadow(false);
panelChildren.appendChild(busyDialog);
//must create zulfile component in foreground UI thread
Component zComponent = null;
if (!Util.isEmpty(dc.getZulFilePath(), true)) {
try {
zComponent = Extensions.getDashboardGadget(dc.getZulFilePath(), panelChildren, dc);
} catch (Exception e) {
throw new AdempiereException(e);
}
}
final Component zulComponent = zComponent;
ContextRunnable cr = new ContextRunnable() {
@Override
protected void doRun() {
try {
asyncRenderGadgetPanel(spt, dc, fp, appDesktop, contextPath, panelChildren, zulComponent);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Adempiere.getThreadPoolExecutor().submit(cr);
}
}
if (dps.length == 0)
{
dashboardLineLayout = new Hlayout();
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, "0");
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, true);
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, "0");
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, true);
dashboardLineLayout.setSclass("dashboard-row");
Anchorchildren dashboardColumn = new Anchorchildren();
dashboardColumn.setAnchor((width-5) + "%" + " 100%");
@ -539,9 +632,9 @@ public class DashboardController implements EventListener<Event> {
// additional row
dashboardLineLayout = new Hlayout();
ZKUpdateUtil.setWidth(dashboardLineLayout, "100%");
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, currentLineNo + 1);
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, true);
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, currentLineNo + 1);
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, true);
dashboardLineLayout.setSclass("dashboard-row");
Anchorchildren dashboardLine = new Anchorchildren();
dashboardLine.setAnchor(width + "% 1%");
@ -564,9 +657,9 @@ public class DashboardController implements EventListener<Event> {
}
//
if (!update && !dashboardRunnable.isEmpty())
if (!update)
{
startDashboardRunnable(parent);
startDashboardRunnable(parent);
}
}
@ -585,18 +678,20 @@ public class DashboardController implements EventListener<Event> {
}
/**
*
* @param content
* @param dc
* Create gadget components in background thread
* @param dashboardContent
* @param dashboardRunnable
* @return
* @param appDesktop
* @param contextPath
* @param parentComponent
* @param components
* @param zulComponent
* @throws Exception
*/
public boolean render(Component content, MDashboardContent dc, DashboardRunnable dashboardRunnable) throws Exception {
boolean empty = true;
private void asyncRenderComponents(MDashboardContent dashboardContent, DashboardRunnable dashboardRunnable, IDesktop appDesktop, String contextPath,
HtmlBasedComponent parentComponent, List<Component> components, Component zulComponent) throws Exception {
// HTML content
String htmlContent = dc.get_ID() > 0 ? dc.get_Translation(MDashboardContent.COLUMNNAME_HTML) : null;
String htmlContent = dashboardContent.get_ID() > 0 ? dashboardContent.get_Translation(MDashboardContent.COLUMNNAME_HTML) : null;
if(htmlContent != null)
{
StringBuilder result = new StringBuilder("<html><head>");
@ -614,8 +709,7 @@ public class DashboardController implements EventListener<Event> {
result.append("</style>");
} catch (Exception e1) {
logger.log(Level.SEVERE, e1.getLocalizedMessage(), e1);
}
finally{
} finally{
if (bufferedReader != null) {
try {
bufferedReader.close();
@ -629,56 +723,58 @@ public class DashboardController implements EventListener<Event> {
Html html = new Html();
html.setContent(result.toString());
content.appendChild(html);
empty = false;
components.add(html);
}
// Window
int AD_Window_ID = dc.getAD_Window_ID();
int AD_Window_ID = dashboardContent.getAD_Window_ID();
if(AD_Window_ID > 0)
{
int AD_Menu_ID = dc.getAD_Menu_ID();
int AD_Menu_ID = dashboardContent.getAD_Menu_ID();
Div div = new Div();
ToolBarButton btn = new ToolBarButton(String.valueOf(AD_Menu_ID));
I_AD_Menu menu = dc.getAD_Menu();
I_AD_Menu menu = dashboardContent.getAD_Menu();
btn.setLabel(menu.getName());
btn.setAttribute("AD_Menu_ID", AD_Menu_ID);
btn.addEventListener(Events.ON_CLICK, this);
div.appendChild(btn);
content.appendChild(div);
empty = false;
components.add(div);
}
//Report & Process
int AD_Process_ID = dc.getAD_Process_ID();
int AD_Process_ID = dashboardContent.getAD_Process_ID();
if(AD_Process_ID > 0)
{
String sql = "SELECT AD_MENU_ID FROM AD_MENU WHERE AD_Process_ID=?";
int AD_Menu_ID = DB.getSQLValue(null, sql, AD_Process_ID);
int AD_Menu_ID = DB.getSQLValueEx(null, sql, AD_Process_ID);
ToolBarButton btn = new ToolBarButton();
MMenu menu = new MMenu(Env.getCtx(), AD_Menu_ID, null);
btn.setAttribute("AD_Menu_ID", AD_Menu_ID);
btn.addEventListener(Events.ON_CLICK, this);
empty = false;
if (dc.isEmbedReportContent())
if (dashboardContent.isEmbedReportContent())
{
String processParameters = dc.getProcessParameters();
String processParameters = dashboardContent.getProcessParameters();
Div layout = new Div();
layout.setHeight("100%");
layout.setStyle("display: flex;flex-direction: column;");
components.add(layout);
Iframe iframe = new Iframe();
iframe.setSclass("dashboard-report-iframe");
content.appendChild(iframe);
iframe.setContent(generateReport(AD_Process_ID, dc.getAD_PrintFormat_ID(), processParameters));
iframe.setStyle("flex-grow: 1;");
layout.appendChild(iframe);
iframe.setContent(generateReport(AD_Process_ID, dashboardContent.getAD_PrintFormat_ID(), processParameters, appDesktop, contextPath));
Toolbar toolbar = new Toolbar();
content.appendChild(toolbar);
layout.appendChild(toolbar);
btn.setLabel(Msg.getMsg(Env.getCtx(), "OpenRunDialog"));
toolbar.appendChild(btn);
btn = new ToolBarButton();
btn.setAttribute("AD_Process_ID", AD_Process_ID);
btn.setAttribute("ProcessParameters", processParameters);
btn.setAttribute("AD_PrintFormat_ID", dc.getAD_PrintFormat_ID());
btn.setAttribute("AD_PrintFormat_ID", dashboardContent.getAD_PrintFormat_ID());
btn.addEventListener(Events.ON_CLICK, this);
btn.setLabel(Msg.getMsg(Env.getCtx(), "ViewReportInNewTab"));
toolbar.appendChild(new Separator("vertical"));
@ -692,32 +788,30 @@ public class DashboardController implements EventListener<Event> {
else
btn.setImage(ThemeManager.getThemeResource("images/Refresh16.png"));
btn.addEventListener(Events.ON_CLICK, e -> iframe.setContent(generateReport(AD_Process_ID, dc.getAD_PrintFormat_ID(), processParameters)));
toolbar.appendChild(btn);
btn.addEventListener(Events.ON_CLICK, e -> iframe.setContent(generateReport(AD_Process_ID, dashboardContent.getAD_PrintFormat_ID(), processParameters, appDesktop, contextPath)));
toolbar.appendChild(btn);
}
else
{
btn.setLabel(menu.getName());
content.appendChild(btn);
components.add(btn);
}
}
// Goal
int PA_Goal_ID = dc.getPA_Goal_ID();
int PA_Goal_ID = dashboardContent.getPA_Goal_ID();
if(PA_Goal_ID > 0)
{
String goalDisplay = dc.getGoalDisplay();
String goalDisplay = dashboardContent.getGoalDisplay();
MGoal goal = new MGoal(Env.getCtx(), PA_Goal_ID, null);
if(MDashboardContent.GOALDISPLAY_GaugeIndicator.equals(goalDisplay)) {
WPerformanceIndicator.Options options = new WPerformanceIndicator.Options();
options.colorMap = new HashMap<String, Color>();
options.colorMap.put(WPerformanceIndicator.DIAL_BACKGROUND, new Color(224, 224, 224, 1));
WPAWidget paWidget = new WPAWidget(goal, options, dc.isShowTitle());
((HtmlBasedComponent)content).setSclass("performance-gadget");
content.appendChild(paWidget);
}
else {
WPAWidget paWidget = new WPAWidget(goal, options, dashboardContent.isShowTitle());
components.add(paWidget);
LayoutUtils.addSclass("performance-gadget", parentComponent);
} else {
//link to open performance detail
Div div = new Div();
Toolbarbutton link = new Toolbarbutton();
@ -734,57 +828,46 @@ public class DashboardController implements EventListener<Event> {
}
});
div.appendChild(link);
content.appendChild(div);
components.add(div);
WGraph graph = new WGraph(goal, 55, false, true,
!(MDashboardContent.GOALDISPLAY_Chart.equals(goalDisplay)),
MDashboardContent.GOALDISPLAY_Chart.equals(goalDisplay));
content.appendChild(graph);
components.add(graph);
}
empty = false;
}
// ZUL file url
String url = dc.getZulFilePath();
if(url != null)
// Component created from ZUL file url
if(zulComponent != null)
{
try {
Component component = Extensions.getDashboardGadget(url, content, dc);
if(component != null)
{
if (component instanceof Include)
component = component.getFirstChild();
if (component instanceof DashboardPanel)
{
DashboardPanel dashboardPanel = (DashboardPanel) component;
if (!dashboardPanel.getChildren().isEmpty()) {
content.appendChild(dashboardPanel);
addDashboardPanel(dashboardPanel);
empty = false;
}
if (zulComponent instanceof Include)
zulComponent = zulComponent.getFirstChild();
if (zulComponent instanceof DashboardPanel)
{
DashboardPanel dashboardPanel = (DashboardPanel) zulComponent;
if (!dashboardPanel.getChildren().isEmpty()) {
components.add(dashboardPanel);
addDashboardPanel(dashboardPanel);
}
else
{
content.appendChild(component);
empty = false;
}
}
}
else
{
components.add(zulComponent);
}
} catch (Exception e) {
logger.log(Level.WARNING, "Failed to create components. zul="+url, e);
throw new AdempiereException(e);
}
}
//chart
final int AD_Chart_ID = dc.getAD_Chart_ID();
final int AD_Chart_ID = dashboardContent.getAD_Chart_ID();
if (AD_Chart_ID > 0) {
final Div chartPanel = new Div();
chartPanel.setSclass("chart-gadget");
final MChart chartModel = new MChart(Env.getCtx(), AD_Chart_ID, null);
content.appendChild(chartPanel);
empty = false;
components.add(chartPanel);
chartPanel.addEventListener(Events.ON_AFTER_SIZE, new EventListener<AfterSizeEvent>() {
@Override
public void onEvent(AfterSizeEvent event) throws Exception {
@ -798,13 +881,13 @@ public class DashboardController implements EventListener<Event> {
chartPanel.getChildren().clear();
ChartModel model = new ChartModel();
model.chart = chartModel;
renderChart(chartPanel, width, height, model, dc.isShowTitle());
renderChart(chartPanel, width, height, model, dashboardContent.isShowTitle());
}
});
}
// Status Line
final int AD_StatusLine_ID = dc.getAD_StatusLine_ID();
final int AD_StatusLine_ID = dashboardContent.getAD_StatusLine_ID();
if(AD_StatusLine_ID > 0) {
MStatusLine sl = new MStatusLine(Env.getCtx(), AD_StatusLine_ID, null);
final Html statusLineHtml = new Html();
@ -812,11 +895,49 @@ public class DashboardController implements EventListener<Event> {
Div div = new Div();
div.appendChild(statusLineHtml);
div.setSclass("statusline-gadget");
((HtmlBasedComponent) content.getParent()).setSclass("statusline-wrapper");
content.appendChild(div);
empty = false;
components.add(div);
LayoutUtils.addSclass("statusline-wrapper", ((HtmlBasedComponent) parentComponent.getParent()));
}
}
/**
* Synchronous render of gadget content in foreground ui thread
* @param content must be an instanceof {@link HtmlBasedComponent}
* @param dashboardContent
* @param dashboardRunnable
* @return true if gadget dashboard is not empty
* @throws Exception
*/
public boolean render(Component content, MDashboardContent dashboardContent, DashboardRunnable dashboardRunnable) throws Exception {
List<Component> components = new ArrayList<>();
Component zulComponent = null;
if (!Util.isEmpty(dashboardContent.getZulFilePath(), true)) {
try {
zulComponent = Extensions.getDashboardGadget(dashboardContent.getZulFilePath(), content, dashboardContent);
} catch (Exception e) {
throw new AdempiereException(e);
}
}
HtmlBasedComponent parentComponent = (HtmlBasedComponent) content;
asyncRenderComponents(dashboardContent, dashboardRunnable, SessionManager.getAppDesktop(), Executions.getCurrent().getContextPath(), parentComponent, components, zulComponent);
boolean empty = components.isEmpty();
ServerPushTemplate spt = new ServerPushTemplate(content.getDesktop());
for(Component c : components) {
if (c.getParent() != parentComponent) {
parentComponent.appendChild(c);
}
if (c instanceof DashboardPanel) {
DashboardPanel dpanel = (DashboardPanel) c;
if (dpanel.isLazy()) {
try {
dpanel.refresh(spt);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}
}
return !empty;
}
@ -866,8 +987,8 @@ public class DashboardController implements EventListener<Event> {
}
panel.setSclass("dashboard-widget");
//following 2 line needed for restore to size the panel correctly
ZKUpdateUtil.setHflex(panel, (String)panel.getAttribute(FLEX_GROW_ATTRIBUTE));
ZKUpdateUtil.setHeight(panel, "100%");
ZKUpdateUtil.setHflex(panel, (String)panel.getAttribute(FLEX_GROW_ATTRIBUTE));
ZKUpdateUtil.setHeight(panel, "100%");
}
}
else if(eventName.equals(Events.ON_CLICK))
@ -964,7 +1085,7 @@ public class DashboardController implements EventListener<Event> {
if(comp instanceof Panel)
{
Panel panel = (Panel) comp;
Object value = panel.getAttribute(MDashboardPreference.COLUMNNAME_PA_DashboardPreference_ID);
Object value = panel.getAttribute(MDashboardPreference.COLUMNNAME_PA_DashboardPreference_ID);
if (value != null)
{
int PA_DashboardPreference_ID = Integer.parseInt(value.toString());
@ -1064,7 +1185,7 @@ public class DashboardController implements EventListener<Event> {
if (child instanceof Panel)
{
Panel panel = (Panel) child;
value = panel.getAttribute(MDashboardPreference.COLUMNNAME_PA_DashboardPreference_ID);
value = panel.getAttribute(MDashboardPreference.COLUMNNAME_PA_DashboardPreference_ID);
if (value != null)
{
++counter;
@ -1100,9 +1221,9 @@ public class DashboardController implements EventListener<Event> {
// additional column
Vlayout dashboardColumnLayout = new Vlayout();
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, columnNo + 1);
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, true);
dashboardColumnLayout.setAttribute(COLUMN_NO_ATTRIBUTE, columnNo + 1);
dashboardColumnLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardColumnLayout.setAttribute(IS_ADDITIONAL_COLUMN_ATTRIBUTE, true);
Anchorchildren dashboardColumn = new Anchorchildren();
dashboardColumn.setAnchor(extraWidth + "% 100%");
if (!ClientInfo.isMobile()) {
@ -1149,7 +1270,7 @@ public class DashboardController implements EventListener<Event> {
if (child instanceof Panel)
{
Panel panel = (Panel) child;
value = panel.getAttribute(MDashboardPreference.COLUMNNAME_PA_DashboardPreference_ID);
value = panel.getAttribute(MDashboardPreference.COLUMNNAME_PA_DashboardPreference_ID);
if (value != null)
{
int PA_DashboardPreference_ID = Integer.parseInt(value.toString());
@ -1197,9 +1318,9 @@ public class DashboardController implements EventListener<Event> {
// additional row
Hlayout dashboardLineLayout = new Hlayout();
ZKUpdateUtil.setWidth(dashboardLineLayout, "100%");
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, lineNo + 1);
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, true);
dashboardLineLayout.setAttribute(LINE_ATTRIBUTE, lineNo + 1);
dashboardLineLayout.setAttribute(IS_SHOW_IN_DASHBOARD_ATTRIBUTE, isShowInDashboard);
dashboardLineLayout.setAttribute(IS_ADDITIONAL_ROW_ATTRIBUTE, true);
dashboardLineLayout.setSclass("dashboard-row");
Anchorchildren dashboardLine = new Anchorchildren();
dashboardLine.setAnchor(width + "% 1%");
@ -1315,12 +1436,12 @@ public class DashboardController implements EventListener<Event> {
}
private AMedia generateReport(int AD_Process_ID, int AD_PrintFormat_ID, String parameters) throws Exception {
private AMedia generateReport(int AD_Process_ID, int AD_PrintFormat_ID, String parameters, IDesktop appDesktop, String contextPath) throws Exception {
ReportEngine re = runReport(AD_Process_ID, AD_PrintFormat_ID, parameters);
File file = FileUtil.createTempFile(re.getName(), ".html");
re.createHTML(file, false, AEnv.getLanguage(Env.getCtx()), new HTMLExtension(Executions.getCurrent().getContextPath(), "rp",
SessionManager.getAppDesktop().getComponent().getUuid()));
re.createHTML(file, false, AEnv.getLanguage(Env.getCtx()), new HTMLExtension(contextPath, "rp",
appDesktop.getComponent().getUuid()));
return new AMedia(re.getName(), "html", "text/html", file, false);
}
@ -1448,8 +1569,8 @@ public class DashboardController implements EventListener<Event> {
ts = (Timestamp)value;
else
ts = Timestamp.valueOf(value.toString());
SimpleDateFormat dateFormat = DisplayType.getDateFormat(iPara.getDisplayType());
String info = dateFormat.format(ts);
SimpleDateFormat dateFormat = DisplayType.getDateFormat(iPara.getDisplayType());
String info = dateFormat.format(ts);
if (isTo) {
iPara.setP_Date_To(ts);
iPara.setInfo_To(info);

View File

@ -1,6 +1,27 @@
/**
*
*/
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.editor;
import java.util.logging.Level;
@ -139,8 +160,6 @@ public class WDashboardContentEditor extends WEditor {
DashboardRunnable dashboardRunnable = new DashboardRunnable(panel.getDesktop());
dashboardController.render(div, content, dashboardRunnable);
if (!dashboardRunnable.isEmpty())
dashboardRunnable.refreshDashboard(false);
pc.appendChild(div);
}

View File

@ -17,8 +17,13 @@
width: calc(100% - 10px);
}
.dashboard-widget.z-panel {
display: flex;
flex-direction: column;
justify-content: stretch;
}
.dashboard-widget > .z-panel-body {
height: 100%;
flex-grow: 1;
}
.statusline-wrapper {
@ -95,7 +100,6 @@
border: 1px solid lightgray;
margin:auto;
width: 99%;
height: 90%;
}
.favourites-box {