package ro.nextreports.server.web.analysis; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.attributes.AjaxCallListener; import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.feedback.FeedbackMessage; import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.image.ContextImage; import org.apache.wicket.markup.html.link.ResourceLink; import org.apache.wicket.markup.html.panel.EmptyPanel; import org.apache.wicket.markup.html.panel.GenericPanel; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.IRequestCycle; import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.handler.resource.ResourceRequestHandler; import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler; import org.apache.wicket.request.resource.ContentDisposition; import org.apache.wicket.request.resource.IResource.Attributes; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.spring.injection.annot.SpringBean; import org.odlabs.wiquery.core.events.MouseEvent; import org.odlabs.wiquery.core.events.WiQueryAjaxEventBehavior; import org.odlabs.wiquery.core.javascript.JsStatement; import org.odlabs.wiquery.ui.sortable.SortableJavaScriptResourceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.nextreports.engine.util.ObjectCloner; import ro.nextreports.server.domain.Analysis; import ro.nextreports.server.service.AnalysisService; import ro.nextreports.server.service.SecurityService; import ro.nextreports.server.util.AnalysisUtil; import ro.nextreports.server.util.PermissionUtil; import ro.nextreports.server.web.NextServerSession; import ro.nextreports.server.web.analysis.feature.create.CreatePanel; import ro.nextreports.server.web.analysis.feature.export.CsvResource; import ro.nextreports.server.web.analysis.feature.export.XlsResource; import ro.nextreports.server.web.analysis.feature.export.XlsxResource; import ro.nextreports.server.web.analysis.feature.filter.FilterPanel; import ro.nextreports.server.web.analysis.feature.group.GroupPanel; import ro.nextreports.server.web.analysis.feature.paging.PaginatePanel; import ro.nextreports.server.web.analysis.feature.select.ColumnsPanel; import ro.nextreports.server.web.analysis.feature.sort.SortPanel; import ro.nextreports.server.web.analysis.model.SelectedAnalysisModel; import ro.nextreports.server.web.analysis.util.DatabaseUtil; import ro.nextreports.server.web.common.form.FormContentPanel; import ro.nextreports.server.web.common.form.FormPanel; import ro.nextreports.server.web.common.jgrowl.JGrowlAjaxBehavior; import ro.nextreports.server.web.common.util.PreferencesHelper; import ro.nextreports.server.web.core.BasePage; import ro.nextreports.server.web.core.HomePage; import ro.nextreports.server.web.core.section.SectionContext; import ro.nextreports.server.web.core.section.SectionContextConstants; import ro.nextreports.server.web.security.SecurityUtil; public class AnalysisPanel extends GenericPanel<Analysis> { private static final long serialVersionUID = 1L; private AnalysisDataProvider dataProvider; private XlsResource xlsResource; private XlsxResource xlsxResource; private CsvResource csvResource; @SpringBean private AnalysisService analysisService; @SpringBean private SecurityService securityService; private static final Logger LOG = LoggerFactory.getLogger(AnalysisPanel.class); public AnalysisPanel(String id) { super(id, new SelectedAnalysisModel()); setOutputMarkupId(true); addToolbar(); Form<Void> submitForm = new Form<Void>("submitForm"); add(submitForm); dataProvider = new AnalysisDataProvider(getModel()); xlsResource = new XlsResource(dataProvider); xlsxResource = new XlsxResource(dataProvider); csvResource = new CsvResource(dataProvider); submitForm.add(createTablePanel(dataProvider)); addLinks(submitForm); } @Override public void renderHead(IHeaderResponse response) { super.renderHead(response); response.render(JavaScriptHeaderItem.forReference(SortableJavaScriptResourceReference.get())); } private void addToolbar() { IModel<String> toggleImageModel = new LoadableDetachableModel<String>() { private static final long serialVersionUID = 1L; @Override protected String load() { String imagePath = "images/left-gray.png"; Map<String, String> preferences = NextServerSession.get().getPreferences(); boolean isHidden = !PreferencesHelper.getBoolean("analysis.navigationToggle", preferences); if (isHidden) { imagePath = "images/right-gray.png"; } return imagePath; } }; final ContextImage toggle = new ContextImage("toggle", toggleImageModel); toggle.add(new WiQueryAjaxEventBehavior(MouseEvent.CLICK) { private static final long serialVersionUID = 1L; @Override protected void onEvent(AjaxRequestTarget target) { Map<String, String> preferences = NextServerSession.get().getPreferences(); boolean toogle = false; if (preferences.containsKey("analysis.navigationToggle")) { toogle = Boolean.parseBoolean(preferences.get("analysis.navigationToggle")); toogle = !toogle; } preferences.put("analysis.navigationToggle", String.valueOf(toogle)); NextServerSession.get().setPreferences(preferences); AnalysisBrowserPanel browserPanel = findParent(AnalysisBrowserPanel.class); target.add(browserPanel.getAnalysisNavigationPanel()); target.add(toggle); target.add(AnalysisPanel.this); } public JsStatement statement() { return null; } }); IModel<String> toggleTooltipModel = new LoadableDetachableModel<String>() { private static final long serialVersionUID = 1L; @Override protected String load() { String tooltip = getString("DashboardPanel.hide"); Map<String, String> preferences = NextServerSession.get().getPreferences(); boolean isHidden = !PreferencesHelper.getBoolean("analysis.navigationToggle", preferences); if (isHidden) { tooltip = getString("DashboardPanel.show"); } return tooltip; } }; toggle.add(new AttributeModifier("title", toggleTooltipModel)); add(toggle); add(new Label("title", new LoadableDetachableModel<String>() { @Override protected String load() { Analysis analysis = getModelObject(); String title = getString("Analysis.title"); if (analysis != null) { title += " : " + analysis.getName(); } return title; } })); } private Panel createTablePanel(AnalysisDataProvider dataProvider ) { if (dataProvider.isEmpty()) { return new EmptyPanel("tablePanel"); } else { return new AnalysisTablePanel("tablePanel", dataProvider); } } public void changeDataProvider(IModel<Analysis> model, AjaxRequestTarget target) { dataProvider = new AnalysisDataProvider(model); dataProvider.reset(); xlsResource.setProvider(dataProvider); xlsxResource.setProvider(dataProvider); csvResource.setProvider(dataProvider); AnalysisPanel.this.get("submitForm:tablePanel").replaceWith(createTablePanel(dataProvider)); target.add(AnalysisPanel.this); } private void addLinks(Form<Void> submitForm) { submitForm.add(getCreateLink()); submitForm.add(getSelectLink()); submitForm.add(getSortLink()); submitForm.add(getFilterLink()); submitForm.add(getGroupLink()); submitForm.add(getPaginateLink()); //submitForm.add(getCsvLink()); //submitForm.add(getXlsLink()); submitForm.add(getXlsxLink()); submitForm.add(getSaveLink()); submitForm.add(getFreezeLink()); } private AjaxLink<Analysis> getSelectLink() { return new ToolbarLink<Analysis>("selectColumns", "ColumnsPanel.title", 560) { @Override protected FormContentPanel<Analysis> createPanel() { return new ColumnsPanel(AnalysisPanel.this.getModel()) { private static final long serialVersionUID = 1L; @Override public void onOk(AjaxRequestTarget target) { if (getColumns().size() == 0) { error(getString("ColumnsPanel.selectOne")); target.add(getFeedbackPanel()); return; } ModalWindow.closeCurrent(target); List<String> selectedColumns = getColumns(); List<Boolean> sel = new ArrayList<Boolean>(); for (String column : getChoices()) { sel.add(selectedColumns.contains(column)); } AnalysisPanel.this.getModel().getObject().setSelected(sel); AnalysisPanel.this.getModel().getObject().setColumns(getChoices()); // if some columns are deselected and they were used in order by or group by clause, we have to remove // them from sorts and groups DatabaseUtil.removeGroupColumns(selectedColumns, AnalysisPanel.this.getModel().getObject().getGroups()); DatabaseUtil.removeSortColumns(selectedColumns, AnalysisPanel.this.getModel().getObject().getSortProperty(), AnalysisPanel.this.getModel().getObject().getAscending()); changeDataProvider(AnalysisPanel.this.getModel(), target); } }; } }; } private AjaxLink<Analysis> getSortLink() { return new ToolbarLink<Analysis>("sortRows", "SortPanel.title", 500) { @Override protected FormContentPanel<Analysis> createPanel() { return new SortPanel(AnalysisPanel.this.getModel()) { private static final long serialVersionUID = 1L; @Override public void onOk(AjaxRequestTarget target) { if (isEdit()) { error(getString("editMode")); target.add(getFeedbackPanel()); return; } if (getSortProperty().size() == 0) { error(getString("SortPanel.selectOne")); target.add(getFeedbackPanel()); return; } ModalWindow.closeCurrent(target); Analysis analysis = AnalysisPanel.this.getModel().getObject(); analysis.setSortProperty(getSortProperty()); analysis.setAscending(getAscending()); analysis.setFirstSortRemoved(isFirstSortRemoved()); analysis.setChangeFirstSortOrder(isChangeFirstSortOrder()); AnalysisPanel.this.getModel().setObject(analysis); changeDataProvider(AnalysisPanel.this.getModel(), target); } }; } }; } private AjaxLink<Analysis> getFilterLink() { return new ToolbarLink<Analysis>("filterRows", "FilterPanel.title", 400) { @Override protected FormContentPanel<Analysis> createPanel() { return new FilterPanel(AnalysisPanel.this.getModel()) { private static final long serialVersionUID = 1L; @Override public void onOk(AjaxRequestTarget target) { if (isEdit()) { error(getString("editMode")); target.add(getFeedbackPanel()); return; } ModalWindow.closeCurrent(target); Analysis analysis = AnalysisPanel.this.getModel().getObject(); analysis.setFilters(getFilters()); dataProvider.reset(); target.add(AnalysisPanel.this); } }; } }; } private AjaxLink<Analysis> getCreateLink() { return new ToolbarLink<Analysis>("createColumns", "CreatePanel.title", 500) { @Override protected FormContentPanel<Analysis> createPanel() { return new CreatePanel(ObjectCloner.silenceDeepCopy(AnalysisPanel.this.getModel())) { private static final long serialVersionUID = 1L; private boolean deleted = false; @Override public void onOk(AjaxRequestTarget target) { if (isEdit()) { error(getString("editMode")); target.add(getFeedbackPanel()); return; } ModalWindow.closeCurrent(target); Analysis analysis = getAnalysis(); // header is modified AnalysisPanel.this.getModel().setObject(analysis); changeDataProvider(AnalysisPanel.this.getModel(), target); } @Override public void onDelete(Analysis analysis, AjaxRequestTarget target) { AnalysisPanel.this.getModel().setObject(analysis); } @Override public void onCancel(AjaxRequestTarget target) { super.onCancel(target); cancelAdd(); } }; } }; } private AjaxLink<Analysis> getPaginateLink() { return new ToolbarLink<Analysis>("paginate", "PaginatePanel.title", 200) { @Override protected FormContentPanel<Analysis> createPanel() { return new PaginatePanel(AnalysisPanel.this.getModel()) { private static final long serialVersionUID = 1L; @Override public void onOk(AjaxRequestTarget target) { ModalWindow.closeCurrent(target); changeDataProvider(AnalysisPanel.this.getModel(), target); } }; } }; } private AjaxLink<Analysis> getGroupLink() { return new ToolbarLink<Analysis>("groupRows", "GroupPanel.title", 400) { @Override protected FormContentPanel<Analysis> createPanel() { return new GroupPanel(AnalysisPanel.this.getModel()) { private static final long serialVersionUID = 1L; @Override public void onOk(AjaxRequestTarget target) { if (isEdit()) { error(getString("editMode")); target.add(getFeedbackPanel()); return; } ModalWindow.closeCurrent(target); Analysis analysis = AnalysisPanel.this.getModel().getObject(); analysis.setGroups(getGroups()); // keep as selected only columns from groups and created columns with aggregate functions if (!getGroups().isEmpty()) { List<String> keptColumns = new ArrayList<String>(); for (String col : analysis.getColumns()) { if (getGroups().contains(col) || AnalysisUtil.isAggregateColumn(col)) { keptColumns.add(col); } } List<Boolean> sel = new ArrayList<Boolean>(); for (String column : analysis.getColumns()) { sel.add(keptColumns.contains(column)); } analysis.setSelected(sel); } changeDataProvider(AnalysisPanel.this.getModel(), target); } }; } }; } private ResourceLink<CsvResource> getCsvLink() { return new ResourceLink<CsvResource>("csvExport", csvResource) { @Override public boolean isVisible() { return !dataProvider.isEmpty(); } }; } private ResourceLink<XlsResource> getXlsLink() { return new ResourceLink<XlsResource>("xlsExport", xlsResource) { @Override public boolean isVisible() { return !dataProvider.isEmpty(); } }; } private ResourceLink<XlsxResource> getXlsxLink() { return new ResourceLink<XlsxResource>("xlsxExport", xlsxResource) { @Override public boolean isVisible() { return !dataProvider.isEmpty(); } }; } // private AjaxLink<Analysis> getXlsxLink() { // return new AjaxLink<Analysis>("xlsxExport") { // // @Override // public void onClick(AjaxRequestTarget target) { // // String fileName = AnalysisPanel.this.getModelObject().getName(); // xlsxResource.setFileName(fileName); // ResourceReference ref = new XlsxResourceReference(xlsxResource, fileName); // String url = RequestCycle.get().urlFor(ref, null).toString(); // //// url = url + (url.contains("?") ? "&" : "?"); //// url = url + "antiCache=" + System.currentTimeMillis(); // // System.out.println("**** URL = " + url); // // // the timeout is needed to let Wicket release the channel // target.appendJavaScript("setTimeout(\"window.location.href='" + url + "'\", 100);"); // // //ResourceRequestHandler handler = new ResourceRequestHandler(xlsxResource, null); // // //ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(xlsxResource., fileName); // //handler.setContentDisposition(ContentDisposition.ATTACHMENT); // // //RequestCycle.get().scheduleRequestHandlerAfterCurrent(handler); // } // }; // } private AjaxSubmitLink getFreezeLink() { return new AjaxSubmitLink("freeze") { @Override public void onSubmit(AjaxRequestTarget target, Form form) { Analysis analysis = AnalysisPanel.this.getModel().getObject(); Analysis newAnalysis = ObjectCloner.silenceDeepCopy(analysis); newAnalysis.setName(analysis.getName() + " " + UUID.randomUUID()); newAnalysis.setFreezed(true); String addedId = analysisService.addAnalysis(newAnalysis); AnalysisPanel.this.getModel().setObject(newAnalysis); AnalysisBrowserPanel browserPanel = findParent(AnalysisBrowserPanel.class); SectionContext sectionContext = NextServerSession.get().getSectionContext(AnalysisSection.ID); sectionContext.getData().put(SectionContextConstants.SELECTED_ANALYSIS_ID, addedId); browserPanel.getAnalysisPanel().changeDataProvider(new SelectedAnalysisModel(), target); target.add(browserPanel); getSession().getFeedbackMessages().add(new FeedbackMessage(null, getString("freeze.start"), JGrowlAjaxBehavior.INFO_FADE)); setResponsePage(HomePage.class); analysisService.freeze(newAnalysis); } @Override public boolean isVisible() { if (dataProvider.isEmpty() || AnalysisPanel.this.getModel().getObject().isFreezed()) { return false; } if (!SecurityUtil.hasPermission(securityService, PermissionUtil.getWrite(), getModelObject().getId())) { return false; } return true; } @Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { super.updateAjaxAttributes(attributes); attributes.getAjaxCallListeners().add(new AjaxCallListener() { @Override public CharSequence getBeforeHandler(Component cmpnt) { return "$(\"#" + cmpnt.getMarkupId() + "\").hide()"; } }); } }; } private AjaxSubmitLink getSaveLink() { return new AjaxSubmitLink("save") { @Override public void onSubmit(AjaxRequestTarget target, Form form) { Analysis analysis = AnalysisPanel.this.getModel().getObject(); analysisService.modifyAnalysis(analysis); getSession().getFeedbackMessages().add(new FeedbackMessage(null, new StringResourceModel("Analysis.saved", null, new Object[] {analysis.getName()}).getString(), JGrowlAjaxBehavior.INFO_FADE)); setResponsePage(HomePage.class); } @Override public boolean isVisible() { if (dataProvider.isEmpty()) { return false; } if (!SecurityUtil.hasPermission(securityService, PermissionUtil.getWrite(), getModelObject().getId())) { return false; } return true; } }; } private abstract class ToolbarLink<T extends Analysis> extends AjaxLink<Analysis> { private static final long serialVersionUID = 1L; private String title; private int dialogWidth; public ToolbarLink(String id, String title, int dialogWidth) { super(id); this.title = title; this.dialogWidth = dialogWidth; } @Override public void onClick(AjaxRequestTarget target) { ModalWindow dialog = findParent(BasePage.class).getDialog(); dialog.setTitle(getString(title)); dialog.setInitialWidth(dialogWidth); dialog.setUseInitialHeight(false); FormContentPanel<Analysis> panel = createPanel(); FormPanel<Analysis> formPanel = new FormPanel<Analysis>(dialog.getContentId(), panel, true); formPanel.add(AttributeModifier.append("class", "analysisForm")); dialog.setContent(formPanel); dialog.show(target); } protected abstract FormContentPanel<Analysis> createPanel(); @Override public boolean isVisible() { return !dataProvider.isEmpty(); } }; }