/*
* Copyright (c) 2010-2016 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.gui.api.component.result;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.DownloadLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import com.evolveum.midpoint.gui.api.component.BasePanel;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.dialog.Popupable;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
/**
* @author katkav
*/
public class OperationResultPanel extends BasePanel<OpResult> implements Popupable{
private static final long serialVersionUID = 1L;
private static final String ID_DETAILS_BOX = "detailsBox";
private static final String ID_ICON_TYPE = "iconType";
private static final String ID_MESSAGE = "message";
private static final String ID_MESSAGE_LABEL = "messageLabel";
private static final String ID_PARAMS = "params";
private static final String ID_BACKGROUND_TASK = "backgroundTask";
private static final String ID_SHOW_ALL = "showAll";
private static final String ID_HIDE_ALL = "hideAll";
private static final String ID_ERROR_STACK_TRACE = "errorStackTrace";
static final String OPERATION_RESOURCE_KEY_PREFIX = "operation.";
private static final Trace LOGGER = TraceManager.getTrace(OperationResultPanel.class);
public OperationResultPanel(String id, IModel<OpResult> model, Page parentPage) {
super(id, model);
initLayout(parentPage);
}
public void initLayout(Page parentPage) {
WebMarkupContainer detailsBox = new WebMarkupContainer(ID_DETAILS_BOX);
detailsBox.setOutputMarkupId(true);
detailsBox.add(AttributeModifier.append("class", createHeaderCss()));
add(detailsBox);
initHeader(detailsBox);
initDetails(detailsBox, parentPage);
}
private void initHeader(WebMarkupContainer box) {
WebMarkupContainer iconType = new WebMarkupContainer(ID_ICON_TYPE);
iconType.setOutputMarkupId(true);
iconType.add(new AttributeAppender("class", new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
@Override
public String getObject() {
StringBuilder sb = new StringBuilder();
OpResult message = getModelObject();
switch (message.getStatus()) {
case IN_PROGRESS:
case NOT_APPLICABLE:
sb.append(" fa-info");
break;
case SUCCESS:
sb.append(" fa-check");
break;
case FATAL_ERROR:
sb.append(" fa-ban");
break;
case PARTIAL_ERROR:
case UNKNOWN:
case WARNING:
case HANDLED_ERROR:
default:
sb.append(" fa-warning");
}
return sb.toString();
}
}));
box.add(iconType);
Label message = createMessage();
AjaxLink<String> showMore = new AjaxLink<String>(ID_MESSAGE) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
OpResult result = OperationResultPanel.this.getModelObject();
result.setShowMore(!result.isShowMore());
result.setAlreadyShown(false); // hack to be able to expand/collapse OpResult after rendered.
target.add(OperationResultPanel.this);
}
};
showMore.add(message);
box.add(showMore);
AjaxLink<String> backgroundTask = new AjaxLink<String>(ID_BACKGROUND_TASK) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
final OpResult opResult = OperationResultPanel.this.getModelObject();
String oid = opResult.getBackgroundTaskOid();
if (oid == null || !opResult.isBackgroundTaskVisible()) {
return; // just for safety
}
ObjectReferenceType ref = ObjectTypeUtil.createObjectRef(oid, ObjectTypes.TASK);
WebComponentUtil.dispatchToObjectDetailsPage(ref, getPageBase(), false);
}
};
backgroundTask.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return getModelObject().getBackgroundTaskOid() != null
&& getModelObject().isBackgroundTaskVisible();
}
});
box.add(backgroundTask);
AjaxLink<String> showAll = new AjaxLink<String>(ID_SHOW_ALL) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
showHideAll(true, OperationResultPanel.this.getModelObject(), target);
}
};
showAll.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return !OperationResultPanel.this.getModelObject().isShowMore();
}
});
box.add(showAll);
AjaxLink<String> hideAll = new AjaxLink<String>(ID_HIDE_ALL) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
showHideAll(false, OperationResultPanel.this.getModel().getObject(), target);
}
};
hideAll.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return OperationResultPanel.this.getModelObject().isShowMore();
}
});
box.add(hideAll);
AjaxLink<String> close = new AjaxLink<String>("close") {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
close(target);
}
};
box.add(close);
DownloadLink downloadXml = new DownloadLink("downloadXml", new AbstractReadOnlyModel<File>() {
private static final long serialVersionUID = 1L;
@Override
public File getObject() {
String home = getPageBase().getMidpointConfiguration().getMidpointHome();
File f = new File(home, "result");
DataOutputStream dos = null;
try {
dos = new DataOutputStream(new FileOutputStream(f));
dos.writeBytes(OperationResultPanel.this.getModel().getObject().getXml());
} catch (IOException e) {
LOGGER.error("Could not download result: {}", e.getMessage(), e);
} finally {
IOUtils.closeQuietly(dos);
}
return f;
}
});
downloadXml.setDeleteAfterDownload(true);
box.add(downloadXml);
}
public void close(AjaxRequestTarget target) {
this.setVisible(false);
target.add(this);
}
private Label createMessage() {
Label message = null;
if (StringUtils.isNotBlank(getModelObject().getMessage())) {
PropertyModel<String> messageModel = new PropertyModel<String>(getModel(), "message");
message = new Label(ID_MESSAGE_LABEL, messageModel);
} else {
message = new Label(ID_MESSAGE_LABEL, new LoadableModel<Object>() {
private static final long serialVersionUID = 1L;
@Override
protected Object load() {
OpResult result = OperationResultPanel.this.getModelObject();
String resourceKey = OPERATION_RESOURCE_KEY_PREFIX + result.getOperation();
return getPage().getString(resourceKey, null, resourceKey);
}
});
}
//message.setRenderBodyOnly(true);
message.setOutputMarkupId(true);
return message;
}
private void initDetails(WebMarkupContainer box, Page parentPage) {
final WebMarkupContainer details = new WebMarkupContainer("details", getModel());
details.setOutputMarkupId(true);
details.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return getModel().getObject().isShowMore();
}
});
box.add(details);
WebMarkupContainer operationPanel = new WebMarkupContainer("type");
operationPanel.setOutputMarkupId(true);
operationPanel.add(new AttributeAppender("class", new LoadableModel<String>() {
private static final long serialVersionUID = 1L;
@Override
protected String load() {
return getLabelCss(getModel());
}
}, " "));
details.add(operationPanel);
Label operationLabel = new Label("operationLabel",
parentPage.getString("FeedbackAlertMessageDetails.operation"));
operationLabel.setOutputMarkupId(true);
operationPanel.add(operationLabel);
Label operation = new Label("operation", new LoadableModel<Object>() {
private static final long serialVersionUID = 1L;
@Override
protected Object load() {
OpResult result = getModelObject();
String resourceKey = OPERATION_RESOURCE_KEY_PREFIX + result.getOperation();
return getPage().getString(resourceKey, null, resourceKey);
}
});
operation.setOutputMarkupId(true);
operationPanel.add(operation);
Label count = new Label("countLabel", parentPage.getString("FeedbackAlertMessageDetails.count"));
count.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
OpResult result = getModelObject();
return result.getCount() > 1;
}
});
operationPanel.add(count);
operationPanel.add(initCountPanel(getModel()));
Label message = new Label("resultMessage",
new PropertyModel<String>(getModel(), "message").getObject());// PageBase.new
// PropertyModel<String>(model,
// "message"));
message.setOutputMarkupId(true);
message.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return StringUtils.isNotBlank(getModelObject().getMessage());
}
});
operationPanel.add(message);
Label messageLabel = new Label("messageLabel", parentPage.getString("FeedbackAlertMessageDetails.message"));
messageLabel.setOutputMarkupId(true);
messageLabel.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return StringUtils.isNotBlank(getModelObject().getMessage());
}
});
operationPanel.add(messageLabel);
initParams(operationPanel, getModel(), parentPage);
initContexts(operationPanel, getModel(), parentPage);
initError(operationPanel, getModel(), parentPage);
}
private void initParams(WebMarkupContainer operationContent, final IModel<OpResult> model, Page parentPage) {
Label paramsLabel = new Label("paramsLabel", parentPage.getString("FeedbackAlertMessageDetails.params"));
paramsLabel.setOutputMarkupId(true);
paramsLabel.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return CollectionUtils.isNotEmpty(model.getObject().getParams());
}
});
operationContent.add(paramsLabel);
ListView<Param> params = new ListView<Param>(ID_PARAMS, createParamsModel(model)) {
private static final long serialVersionUID = 1L;
@Override
protected void populateItem(ListItem<Param> item) {
item.add(new Label("paramName", new PropertyModel<Object>(item.getModel(), "name")));
item.add(new Label("paramValue", new PropertyModel<Object>(item.getModel(), "value")));
}
};
params.setOutputMarkupId(true);
params.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return CollectionUtils.isNotEmpty(model.getObject().getParams());
}
});
operationContent.add(params);
ListView<OpResult> subresults = new ListView<OpResult>("subresults", createSubresultsModel(model)) {
private static final long serialVersionUID = 1L;
@Override
protected void populateItem(final ListItem<OpResult> item) {
Panel subresult = new OperationResultPanel("subresult", item.getModel(), getPage());
subresult.setOutputMarkupId(true);
item.add(subresult);
}
};
subresults.setOutputMarkupId(true);
subresults.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return CollectionUtils.isNotEmpty(model.getObject().getSubresults());
}
});
operationContent.add(subresults);
}
private void initContexts(WebMarkupContainer operationContent, final IModel<OpResult> model, Page parentPage) {
Label contextsLabel = new Label("contextsLabel", parentPage.getString("FeedbackAlertMessageDetails.contexts"));
contextsLabel.setOutputMarkupId(true);
contextsLabel.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return CollectionUtils.isNotEmpty(model.getObject().getContexts());
}
});
operationContent.add(contextsLabel);
ListView<Context> contexts = new ListView<Context>("contexts", createContextsModel(model)) {
private static final long serialVersionUID = 1L;
@Override
protected void populateItem(ListItem<Context> item) {
item.add(new Label("contextName", new PropertyModel<Object>(item.getModel(), "name")));
item.add(new Label("contextValue", new PropertyModel<Object>(item.getModel(), "value")));
}
};
contexts.setOutputMarkupId(true);
contexts.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return CollectionUtils.isNotEmpty(model.getObject().getContexts());
}
});
operationContent.add(contexts);
}
private void initError(WebMarkupContainer operationPanel, final IModel<OpResult> model, Page parentPage) {
Label errorLabel = new Label("errorLabel", parentPage.getString("FeedbackAlertMessageDetails.error"));
errorLabel.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
// return true;
return StringUtils.isNotBlank(model.getObject().getExceptionsStackTrace());
}
});
errorLabel.setOutputMarkupId(true);
operationPanel.add(errorLabel);
Label errorMessage = new Label("errorMessage", new PropertyModel<String>(model, "exceptionMessage"));
errorMessage.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
// return true;
return StringUtils.isNotBlank(model.getObject().getExceptionsStackTrace());
}
});
errorMessage.setOutputMarkupId(true);
operationPanel.add(errorMessage);
final Label errorStackTrace = new Label(ID_ERROR_STACK_TRACE,
new PropertyModel<String>(model, "exceptionsStackTrace"));
errorStackTrace.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
// return true;
return model.getObject().isShowError();
}
});
errorStackTrace.setOutputMarkupId(true);
operationPanel.add(errorStackTrace);
AjaxLink errorStackTraceLink = new AjaxLink("errorStackTraceLink") {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
OpResult result = OperationResultPanel.this.getModelObject();
result.setShowError(!model.getObject().isShowError());
result.setAlreadyShown(false); // hack to be able to expand/collapse OpResult after rendered.
// model.getObject().setShowError(!model.getObject().isShowError());
target.add(OperationResultPanel.this);
}
};
errorStackTraceLink.setOutputMarkupId(true);
errorStackTraceLink.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return StringUtils.isNotBlank(model.getObject().getExceptionsStackTrace());
}
});
operationPanel.add(errorStackTraceLink);
}
private Label initCountPanel(final IModel<OpResult> model) {
Label count = new Label("count", new PropertyModel<String>(model, "count"));
count.add(new VisibleEnableBehaviour() {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
OpResult result = model.getObject();
return result.getCount() > 1;
}
});
return count;
}
private void showHideAll(final boolean show, OpResult opresult, AjaxRequestTarget target) {
opresult.setShowMoreAll(show);
opresult.setAlreadyShown(false); // hack to be able to expand/collapse OpResult after rendered.
target.add(OperationResultPanel.this);
}
private IModel<String> createHeaderCss() {
return new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
@Override
public String getObject() {
OpResult result = getModelObject();
if (result == null || result.getStatus() == null) {
return " box-warning";
}
switch (result.getStatus()) {
case IN_PROGRESS:
case NOT_APPLICABLE:
return " box-info";
case SUCCESS:
return " box-success";
case FATAL_ERROR:
return " box-danger";
case UNKNOWN:
case PARTIAL_ERROR:
case HANDLED_ERROR: // TODO:
case WARNING:
default:
return " box-warning";
}
}
};
}
static IModel<List<Param>> createParamsModel(final IModel<OpResult> model) {
return new LoadableModel<List<Param>>(false) {
private static final long serialVersionUID = 1L;
@Override
protected List<Param> load() {
OpResult result = model.getObject();
return result.getParams();
}
};
}
static IModel<List<Context>> createContextsModel(final IModel<OpResult> model) {
return new LoadableModel<List<Context>>(false) {
private static final long serialVersionUID = 1L;
@Override
protected List<Context> load() {
OpResult result = model.getObject();
return result.getContexts();
}
};
}
private IModel<List<OpResult>> createSubresultsModel(final IModel<OpResult> model) {
return new LoadableModel<List<OpResult>>(false) {
private static final long serialVersionUID = 1L;
@Override
protected List<OpResult> load() {
OpResult result = model.getObject();
List<OpResult> subresults = result.getSubresults();
if (subresults == null) {
subresults = new ArrayList<OpResult>();
}
return subresults;
}
};
}
private String getLabelCss(final IModel<OpResult> model) {
OpResult result = model.getObject();
if (result == null || result.getStatus() == null) {
return "messages-warn-content";
}
switch (result.getStatus()) {
case IN_PROGRESS:
case NOT_APPLICABLE:
return "left-info";
case SUCCESS:
return "left-success";
case FATAL_ERROR:
return "left-danger";
case UNKNOWN:
case PARTIAL_ERROR:
case HANDLED_ERROR: // TODO:
case WARNING:
default:
return "left-warning";
}
}
private String getIconCss(final IModel<OpResult> model) {
OpResult result = model.getObject();
if (result == null || result.getStatus() == null) {
return "fa-warning text-warning";
}
switch (result.getStatus()) {
case IN_PROGRESS:
case NOT_APPLICABLE:
return "fa-info-circle text-info";
case SUCCESS:
return "fa-check-circle-o text-success";
case FATAL_ERROR:
return "fa-times-circle-o text-danger";
case UNKNOWN:
case PARTIAL_ERROR:
case HANDLED_ERROR: // TODO:
case WARNING:
default:
return "fa-warning text-warning";
}
}
static String createMessageTooltip(final IModel<FeedbackMessage> model) {
FeedbackMessage message = model.getObject();
switch (message.getLevel()) {
case FeedbackMessage.INFO:
return "info";
case FeedbackMessage.SUCCESS:
return "success";
case FeedbackMessage.ERROR:
return "partialError";
case FeedbackMessage.FATAL:
return "fatalError";
case FeedbackMessage.UNDEFINED:
return "undefined";
case FeedbackMessage.DEBUG:
return "debug";
case FeedbackMessage.WARNING:
default:
return "warn";
}
}
@Override
public int getWidth() {
return 900;
}
@Override
public int getHeight() {
return 500;
}
@Override
public StringResourceModel getTitle() {
return new StringResourceModel("OperationResultPanel.result");
}
@Override
public Component getComponent() {
return this;
}
}