/* * Copyright (c) 2010-2015 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.web.page.admin.reports; import com.evolveum.midpoint.gui.api.model.LoadableModel; import com.evolveum.midpoint.gui.api.page.PageBase; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.match.PolyStringNormMatchingRule; import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer; import com.evolveum.midpoint.prism.query.*; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterEntry; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.report.api.ReportManager; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.application.AuthorizationAction; import com.evolveum.midpoint.web.application.PageDescriptor; import com.evolveum.midpoint.web.component.AjaxDownloadBehaviorFromStream; import com.evolveum.midpoint.web.component.BasicSearchPanel; import com.evolveum.midpoint.web.component.DateLabelComponent; import com.evolveum.midpoint.web.component.data.BoxedTablePanel; import com.evolveum.midpoint.web.component.data.ObjectDataProvider; import com.evolveum.midpoint.web.component.data.Table; import com.evolveum.midpoint.web.component.data.column.CheckBoxHeaderColumn; import com.evolveum.midpoint.web.component.data.column.InlineMenuHeaderColumn; import com.evolveum.midpoint.web.component.data.column.InlineMenuable; import com.evolveum.midpoint.web.component.dialog.ConfirmationPanel; import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem; import com.evolveum.midpoint.web.component.util.SelectableBean; import com.evolveum.midpoint.web.page.admin.configuration.PageAdminConfiguration; import com.evolveum.midpoint.web.page.admin.configuration.component.HeaderMenuAction; import com.evolveum.midpoint.web.page.admin.reports.component.DownloadButtonPanel; import com.evolveum.midpoint.web.page.admin.reports.dto.ReportDeleteDialogDto; import com.evolveum.midpoint.web.page.admin.reports.dto.ReportOutputSearchDto; import com.evolveum.midpoint.web.page.admin.users.dto.UsersDto; import com.evolveum.midpoint.web.session.ReportsStorage; import com.evolveum.midpoint.web.session.UserProfileStorage; import com.evolveum.midpoint.web.util.OnePageParameterEncoder; import com.evolveum.midpoint.xml.ns._public.common.common_3.ExportType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportOutputType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType; import org.apache.commons.lang.StringUtils; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.ChoiceRenderer; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.util.string.StringValue; import java.io.InputStream; import java.util.*; /** * @author lazyman */ @PageDescriptor(url = "/admin/reports/created", action = { @AuthorizationAction(actionUri = PageAdminReports.AUTH_REPORTS_ALL, label = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_LABEL, description = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_DESCRIPTION), @AuthorizationAction(actionUri = AuthorizationConstants.AUTZ_UI_REPORTS_CREATED_REPORTS_URL, label = "PageCreatedReports.auth.createdReports.label", description = "PageCreatedReports.auth.createdReports.description")}) public class PageCreatedReports extends PageAdminReports { private static final Trace LOGGER = TraceManager.getTrace(PageCreatedReports.class); private static final String DOT_CLASS = PageCreatedReports.class.getName() + "."; private static final String OPERATION_DELETE = DOT_CLASS + "deleteReportOutput"; private static final String OPERATION_DOWNLOAD_REPORT = DOT_CLASS + "downloadReport"; private static final String OPERATION_GET_REPORT_FILENAME = DOT_CLASS + "getReportFilename"; private static final String ID_MAIN_FORM = "mainForm"; private static final String ID_CREATED_REPORTS_TABLE = "table"; private static final String ID_SEARCH_FORM = "searchForm"; private static final String ID_BASIC_SEARCH = "basicSearch"; private static final String ID_REPORT_TYPE_SELECT = "reportType"; private static final String ID_TABLE_HEADER = "tableHeader"; private LoadableModel<ReportOutputSearchDto> searchModel; private IModel<ReportDeleteDialogDto> deleteModel = new Model<>(); private ReportOutputType currentReport; private static Map<ExportType, String> reportExportTypeMap = new HashMap<>(); static { reportExportTypeMap.put(ExportType.CSV, "text/csv; charset=UTF-8"); reportExportTypeMap.put(ExportType.DOCX, "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8"); reportExportTypeMap.put(ExportType.HTML, "text/html; charset=UTF-8"); reportExportTypeMap.put(ExportType.ODS, "application/vnd.oasis.opendocument.spreadsheet; charset=UTF-8"); reportExportTypeMap.put(ExportType.ODT, "application/vnd.oasis.opendocument.text; charset=UTF-8"); reportExportTypeMap.put(ExportType.PDF, "application/pdf; charset=UTF-8"); reportExportTypeMap.put(ExportType.PPTX, "application/vnd.openxmlformats-officedocument.presentationml.presentation; charset=UTF-8"); reportExportTypeMap.put(ExportType.RTF, "application/rtf; charset=UTF-8"); reportExportTypeMap.put(ExportType.XHTML, "application/xhtml+xml; charset=UTF-8"); reportExportTypeMap.put(ExportType.XLS, "application/vnd.ms-excel; charset=UTF-8"); reportExportTypeMap.put(ExportType.XLSX, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8"); reportExportTypeMap.put(ExportType.XML, "application/xml; charset=UTF-8"); reportExportTypeMap.put(ExportType.XML_EMBED, "text/xml; charset=UTF-8"); } public PageCreatedReports(PageParameters pageParameters) { super(pageParameters); searchModel = new LoadableModel<ReportOutputSearchDto>(false) { @Override protected ReportOutputSearchDto load() { ReportsStorage storage = getSessionStorage().getReports(); ReportOutputSearchDto dto = storage.getReportOutputSearch(); if (dto != null) { return dto; } return createSearchDto(); } }; initLayout(); } private ReportOutputSearchDto createSearchDto() { ReportOutputSearchDto dto = new ReportOutputSearchDto(); Map<String, String> reportTypeMap = dto.getReportTypeMap(); List<PrismObject<ReportType>> reportTypes = WebModelServiceUtils.searchObjects(ReportType.class, null, null, getPageBase()); LOGGER.debug("Found {} report types.", reportTypes.size()); for (PrismObject o : reportTypes) { ReportType reportType = (ReportType) o.asObjectable(); if (reportType.isParent()) { String name = WebComponentUtil.getName(o); reportTypeMap.put(name, reportType.getOid()); } } StringValue param = getPage().getPageParameters().get(OnePageParameterEncoder.PARAMETER); if (param != null) { for (String key : dto.getReportTypeMap().keySet()) { if (reportTypeMap.get(key).equals(param.toString())) { dto.setReportType(key); } } } return dto; } private void initLayout() { Form mainForm = new Form(ID_MAIN_FORM); add(mainForm); final AjaxDownloadBehaviorFromStream ajaxDownloadBehavior = new AjaxDownloadBehaviorFromStream() { @Override protected InputStream initStream() { return createReport(this); } @Override public String getFileName(){ return getReportFileName(); } }; mainForm.add(ajaxDownloadBehavior); ObjectDataProvider provider = new ObjectDataProvider(PageCreatedReports.this, ReportOutputType.class) { @Override protected void saveProviderPaging(ObjectQuery query, ObjectPaging paging) { ReportsStorage storage = getSessionStorage().getReports(); storage.setReportOutputsPaging(paging); } @Override public ObjectQuery getQuery() { return createQuery(); } }; BoxedTablePanel table = new BoxedTablePanel(ID_CREATED_REPORTS_TABLE, provider, initColumns(ajaxDownloadBehavior), UserProfileStorage.TableId.PAGE_CREATED_REPORTS_PANEL, (int) getItemsPerPage(UserProfileStorage.TableId.PAGE_CREATED_REPORTS_PANEL)) { @Override protected WebMarkupContainer createHeader(String headerId) { return new SearchFragment(headerId, ID_TABLE_HEADER, PageCreatedReports.this, searchModel); } }; table.setShowPaging(true); table.setOutputMarkupId(true); mainForm.add(table); } //TODO - commented until FileType property will be available in ReportOutputType public PageBase getPageBase() { return (PageBase) getPage(); } //TODO - consider adding Author name, File Type and ReportType to columns private List<IColumn<SelectableBean<ReportOutputType>, String>> initColumns( final AjaxDownloadBehaviorFromStream ajaxDownloadBehavior) { List<IColumn<SelectableBean<ReportOutputType>, String>> columns = new ArrayList<>(); IColumn column; column = new CheckBoxHeaderColumn(); columns.add(column); column = new PropertyColumn(createStringResource("pageCreatedReports.table.name"), "name", "value.name"); columns.add(column); column = new PropertyColumn(createStringResource("pageCreatedReports.table.description"), "value.description"); columns.add(column); column = new AbstractColumn<SelectableBean<ReportOutputType>, String>( createStringResource("pageCreatedReports.table.time"), "createTimestamp") { @Override public void populateItem(Item<ICellPopulator<SelectableBean<ReportOutputType>>> cellItem, String componentId, final IModel<SelectableBean<ReportOutputType>> rowModel) { cellItem.add(new DateLabelComponent(componentId, new AbstractReadOnlyModel<Date>() { @Override public Date getObject() { ReportOutputType object = rowModel.getObject().getValue(); MetadataType metadata = object != null ? object.getMetadata() : null; if (metadata == null) { return null; } return XmlTypeConverter.toDate(metadata.getCreateTimestamp()); } }, DateLabelComponent.LONG_MEDIUM_STYLE)); } }; columns.add(column); column = new AbstractColumn<SelectableBean<ReportOutputType>, String>(new Model(), null) { @Override public void populateItem(Item<ICellPopulator<SelectableBean<ReportOutputType>>> cellItem, String componentId, final IModel<SelectableBean<ReportOutputType>> model) { DownloadButtonPanel panel = new DownloadButtonPanel(componentId) { @Override protected void deletePerformed(AjaxRequestTarget target) { deleteSelectedPerformed(target, ReportDeleteDialogDto.Operation.DELETE_SINGLE, model.getObject().getValue()); } @Override protected void downloadPerformed(AjaxRequestTarget target) { currentReport = model.getObject().getValue(); PageCreatedReports.this. downloadPerformed(target, model.getObject().getValue(), ajaxDownloadBehavior); } }; cellItem.add(panel); } }; columns.add(column); column = new InlineMenuHeaderColumn<InlineMenuable>(initInlineMenu()) { @Override public void populateItem(Item<ICellPopulator<InlineMenuable>> cellItem, String componentId, IModel<InlineMenuable> rowModel) { cellItem.add(new Label(componentId)); } }; columns.add(column); return columns; } private List<InlineMenuItem> initInlineMenu() { List<InlineMenuItem> headerMenuItems = new ArrayList<>(); headerMenuItems.add(new InlineMenuItem(createStringResource("pageCreatedReports.inlineMenu.deleteAll"), true, new HeaderMenuAction(this) { @Override public void onSubmit(AjaxRequestTarget target, Form<?> form) { deleteAllPerformed(target, ReportDeleteDialogDto.Operation.DELETE_ALL); } } )); headerMenuItems.add(new InlineMenuItem(createStringResource("pageCreatedReports.inlineMenu.deleteSelected"), true, new HeaderMenuAction(this) { @Override public void onSubmit(AjaxRequestTarget target, Form<?> form) { deleteSelectedPerformed(target, ReportDeleteDialogDto.Operation.DELETE_SELECTED, null); } } )); return headerMenuItems; } private IModel<String> createDeleteConfirmString() { return new AbstractReadOnlyModel<String>() { @Override public String getObject() { ReportDeleteDialogDto dto = deleteModel.getObject(); switch (dto.getOperation()) { case DELETE_SINGLE: ReportOutputType report = dto.getObjects().get(0); return createStringResource("pageCreatedReports.message.deleteOutputSingle", report.getName().getOrig()).getString(); case DELETE_ALL: return createStringResource("pageCreatedReports.message.deleteAll").getString(); default: return createStringResource("pageCreatedReports.message.deleteOutputConfirmed", getSelectedData().size()).getString(); } } }; } private List<ReportOutputType> getSelectedData() { ObjectDataProvider<SelectableBean<ReportOutputType>, ReportOutputType> provider = getReportDataProvider(); List<SelectableBean<ReportOutputType>> rows = provider.getAvailableData(); List<ReportOutputType> selected = new ArrayList<>(); for (SelectableBean<ReportOutputType> row : rows) { if (row.isSelected() && row.getValue() != null) { selected.add(row.getValue()); } } return selected; } private ObjectDataProvider<SelectableBean<ReportOutputType>, ReportOutputType> getReportDataProvider() { DataTable table = getReportOutputTable().getDataTable(); return (ObjectDataProvider<SelectableBean<ReportOutputType>, ReportOutputType>) table.getDataProvider(); } private Table getReportOutputTable() { return (Table) get(createComponentPath(ID_MAIN_FORM, ID_CREATED_REPORTS_TABLE)); } private ObjectDataProvider getTableDataProvider() { Table tablePanel = getReportOutputTable(); DataTable table = tablePanel.getDataTable(); return (ObjectDataProvider) table.getDataProvider(); } private void deleteAllPerformed(AjaxRequestTarget target, ReportDeleteDialogDto.Operation op) { final ReportDeleteDialogDto dto = new ReportDeleteDialogDto(op, null); deleteModel.setObject(dto); getPageBase().showMainPopup(getDeleteDialogPanel(), target); } private ConfirmationPanel getDeleteDialogPanel(){ ConfirmationPanel dialog = new ConfirmationPanel(getPageBase().getMainPopupBodyId(), createDeleteConfirmString()){ @Override public void yesPerformed(AjaxRequestTarget target) { getPageBase().hideMainPopup(target); ReportDeleteDialogDto dto = deleteModel.getObject(); switch (dto.getOperation()) { case DELETE_SINGLE: deleteSelectedConfirmedPerformed(target, Arrays.asList(dto.getObjects().get(0))); break; case DELETE_SELECTED: deleteSelectedConfirmedPerformed(target, dto.getObjects()); break; case DELETE_ALL: deleteAllConfirmedPerformed(target); break; } } }; return dialog; } private void deleteSelectedPerformed(AjaxRequestTarget target, ReportDeleteDialogDto.Operation op, ReportOutputType single) { List<ReportOutputType> selected = getSelectedData(); if (single != null) { selected.clear(); selected.add(single); } if (selected.isEmpty()) { return; } ReportDeleteDialogDto dto = new ReportDeleteDialogDto(op, selected); deleteModel.setObject(dto); getPageBase().showMainPopup(getDeleteDialogPanel(), target); } private void deleteSelectedConfirmedPerformed(AjaxRequestTarget target, List<ReportOutputType> objects) { OperationResult result = new OperationResult(OPERATION_DELETE); for (ReportOutputType output : objects) { WebModelServiceUtils.deleteObject(ReportOutputType.class, output.getOid(), result, this); } result.computeStatusIfUnknown(); ObjectDataProvider provider = getTableDataProvider(); provider.clearCache(); showResult(result); target.add((Component) getReportOutputTable()); target.add(getFeedbackPanel()); } private void deleteAllConfirmedPerformed(AjaxRequestTarget target) { //TODO - implement as background task warn("Not implemented yet, will be implemented as background task."); target.add(getFeedbackPanel()); } private ObjectQuery createQuery() { ReportOutputSearchDto dto = searchModel.getObject(); S_AtomicFilterEntry q = QueryBuilder.queryFor(ReportOutputType.class, getPrismContext()); if (StringUtils.isNotEmpty(dto.getText())) { PolyStringNormalizer normalizer = getPrismContext().getDefaultPolyStringNormalizer(); String normalizedString = normalizer.normalize(dto.getText()); q = q.item(ReportOutputType.F_NAME).containsPoly(normalizedString).matchingNorm().and(); } String oid = dto.getReportTypeMap().get(dto.getReportType()); if (StringUtils.isNotEmpty(oid)) { q = q.item(ReportOutputType.F_REPORT_REF).ref(oid).and(); } return q.all().build(); } private InputStream createReport(AjaxDownloadBehaviorFromStream ajaxDownloadBehaviorFromStream) { return createReport(currentReport, ajaxDownloadBehaviorFromStream, this); } public static InputStream createReport(ReportOutputType report, AjaxDownloadBehaviorFromStream ajaxDownloadBehaviorFromStream, PageBase pageBase) { OperationResult result = new OperationResult(OPERATION_DOWNLOAD_REPORT); ReportManager reportManager = pageBase.getReportManager(); if (report == null) { return null; } String contentType = reportExportTypeMap.get(report.getExportType()); if (StringUtils.isEmpty(contentType)) { contentType = "multipart/mixed; charset=UTF-8"; } ajaxDownloadBehaviorFromStream.setContentType(contentType); InputStream input = null; try { input = reportManager.getReportOutputData(report.getOid(), result); } catch (Exception e) { pageBase.error(pageBase.getString("pageCreatedReports.message.downloadError") + " " + e.getMessage()); LoggingUtils.logUnexpectedException(LOGGER, "Couldn't download report.", e); LOGGER.trace(result.debugDump()); } finally { result.computeStatusIfUnknown(); } if (WebComponentUtil.showResultInPage(result)) { pageBase.showResult(result); } return input; } private void fileTypeFilterPerformed(AjaxRequestTarget target) { //TODO - perform filtering based on file type - need to wait for schema update (ReportOutputType) } private void downloadPerformed(AjaxRequestTarget target, ReportOutputType report, AjaxDownloadBehaviorFromStream ajaxDownloadBehavior) { ajaxDownloadBehavior.initiate(target); } private void searchPerformed(AjaxRequestTarget target) { refreshTable(target); } private void clearSearchPerformed(AjaxRequestTarget target) { ReportOutputSearchDto dto = searchModel.getObject(); dto.setReportType(null); dto.setText(null); refreshTable(target); } private void refreshTable(AjaxRequestTarget target) { Table panel = getReportOutputTable(); ReportsStorage storage = getSessionStorage().getReports(); storage.setReportOutputSearch(searchModel.getObject()); storage.setReportOutputsPaging(null); panel.setCurrentPage(0); target.add((Component) panel); target.add(getFeedbackPanel()); } private static class SearchFragment extends Fragment { public SearchFragment(String id, String markupId, MarkupContainer markupProvider, IModel<ReportOutputSearchDto> model) { super(id, markupId, markupProvider, model); initLayout(); } private void initLayout() { final Form searchForm = new Form(ID_SEARCH_FORM); add(searchForm); searchForm.setOutputMarkupId(true); final IModel<ReportOutputSearchDto> model = (IModel) getDefaultModel(); BasicSearchPanel<ReportOutputSearchDto> basicSearch = new BasicSearchPanel<ReportOutputSearchDto>(ID_BASIC_SEARCH, model) { @Override protected IModel<String> createSearchTextModel() { return new PropertyModel<String>(model, UsersDto.F_TEXT); } @Override protected void searchPerformed(AjaxRequestTarget target) { PageCreatedReports page = (PageCreatedReports) getPage(); page.searchPerformed(target); } @Override protected void clearSearchPerformed(AjaxRequestTarget target) { PageCreatedReports page = (PageCreatedReports) getPage(); page.clearSearchPerformed(target); } }; searchForm.add(basicSearch); DropDownChoice reportTypeSelect = new DropDownChoice(ID_REPORT_TYPE_SELECT, new PropertyModel(model, ReportOutputSearchDto.F_REPORT_TYPE), new PropertyModel(model, ReportOutputSearchDto.F_REPORT_TYPES), new ChoiceRenderer() ) { @Override protected String getNullValidDisplayValue() { return getString("pageCreatedReports.filter.reportType"); } }; reportTypeSelect.add(new OnChangeAjaxBehavior() { @Override protected void onUpdate(AjaxRequestTarget target) { PageCreatedReports page = (PageCreatedReports) getPage(); page.searchPerformed(target); } }); reportTypeSelect.setOutputMarkupId(true); reportTypeSelect.setNullValid(true); searchForm.add(reportTypeSelect); } } private String getReportFileName(){ try { OperationResult result = new OperationResult(OPERATION_GET_REPORT_FILENAME); ReportOutputType reportOutput = WebModelServiceUtils.loadObject(ReportOutputType.class, currentReport.getOid(), getPageBase(), null, result).asObjectable(); String fileName = reportOutput.getFilePath(); if (fileName.contains("/")) { fileName = fileName.substring(fileName.lastIndexOf("/") + 1); } return fileName; } catch (Exception ex){ //nothing to do } return null; } }