/*
* 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.certification;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.exception.SystemException;
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.DateLabelComponent;
import com.evolveum.midpoint.web.component.data.BoxedTablePanel;
import com.evolveum.midpoint.web.component.data.Table;
import com.evolveum.midpoint.web.component.data.column.*;
import com.evolveum.midpoint.web.component.data.column.DoubleButtonColumn.BUTTON_COLOR_CLASS;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem;
import com.evolveum.midpoint.web.page.admin.certification.dto.CertWorkItemDto;
import com.evolveum.midpoint.web.page.admin.certification.dto.CertWorkItemDtoProvider;
import com.evolveum.midpoint.web.page.admin.certification.dto.SearchingUtils;
import com.evolveum.midpoint.web.page.admin.certification.helpers.AvailableResponses;
import com.evolveum.midpoint.web.page.admin.configuration.component.HeaderMenuAction;
import com.evolveum.midpoint.web.session.UserProfileStorage;
import com.evolveum.midpoint.web.util.OnePageParameterEncoder;
import com.evolveum.midpoint.web.util.TooltipBehavior;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationResponseType;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
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.CheckBox;
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.request.mapper.parameter.PageParameters;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static com.evolveum.midpoint.web.page.admin.certification.CertDecisionHelper.WhichObject.OBJECT;
import static com.evolveum.midpoint.web.page.admin.certification.CertDecisionHelper.WhichObject.TARGET;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationResponseType.*;
/**
* @author mederly
*/
@PageDescriptor(url = "/admin/certification/decisions",
action = {
@AuthorizationAction(actionUri = PageAdminCertification.AUTH_CERTIFICATION_ALL,
label = PageAdminCertification.AUTH_CERTIFICATION_ALL_LABEL,
description = PageAdminCertification.AUTH_CERTIFICATION_ALL_DESCRIPTION),
@AuthorizationAction(actionUri = PageAdminCertification.AUTH_CERTIFICATION_DECISIONS,
label = PageAdminCertification.AUTH_CERTIFICATION_DECISIONS_LABEL,
description = PageAdminCertification.AUTH_CERTIFICATION_DECISIONS_DESCRIPTION)})
public class PageCertDecisions extends PageAdminCertification {
private static final Trace LOGGER = TraceManager.getTrace(PageCertDecisions.class);
private static final String DOT_CLASS = PageCertDecisions.class.getName() + ".";
private static final String OPERATION_RECORD_ACTION = DOT_CLASS + "recordAction";
private static final String OPERATION_RECORD_ACTION_SELECTED = DOT_CLASS + "recordActionSelected";
private static final String ID_MAIN_FORM = "mainForm";
private static final String ID_DECISIONS_TABLE = "decisionsTable";
private static final String ID_SEARCH_FORM = "searchForm";
private static final String ID_SHOW_NOT_DECIDED_ONLY = "showNotDecidedOnly";
private static final String ID_TABLE_HEADER = "tableHeader";
CertDecisionHelper helper = new CertDecisionHelper();
private IModel<Boolean> showNotDecidedOnlyModel = new Model<>(false);
public PageCertDecisions() {
initLayout();
}
//region Data
private CertWorkItemDtoProvider createProvider() {
CertWorkItemDtoProvider provider = new CertWorkItemDtoProvider(PageCertDecisions.this);
provider.setQuery(createCaseQuery());
provider.setCampaignQuery(createCampaignQuery());
provider.setReviewerOid(getCurrentUserOid());
provider.setSort(SearchingUtils.CURRENT_REVIEW_DEADLINE, SortOrder.ASCENDING); // default sorting
return provider;
}
private ObjectQuery createCaseQuery() {
return new ObjectQuery();
}
private ObjectQuery createCampaignQuery() {
return new ObjectQuery();
}
private String getCurrentUserOid() {
try {
return getSecurityEnforcer().getPrincipal().getOid();
} catch (SecurityViolationException e) {
// TODO handle more cleanly
throw new SystemException("Couldn't get currently logged user OID", e);
}
}
//endregion
//region Layout
private void initLayout() {
Form mainForm = new Form(ID_MAIN_FORM);
add(mainForm);
CertWorkItemDtoProvider provider = createProvider();
int itemsPerPage = (int) getItemsPerPage(UserProfileStorage.TableId.PAGE_CERT_DECISIONS_PANEL);
BoxedTablePanel table = new BoxedTablePanel(ID_DECISIONS_TABLE, provider, initColumns(),
UserProfileStorage.TableId.PAGE_CERT_DECISIONS_PANEL, itemsPerPage) {
@Override
protected WebMarkupContainer createHeader(String headerId) {
return new SearchFragment(headerId, ID_TABLE_HEADER, PageCertDecisions.this, showNotDecidedOnlyModel);
}
};
table.setShowPaging(true);
table.setOutputMarkupId(true);
table.setItemsPerPage(itemsPerPage); // really don't know why this is necessary, as e.g. in PageRoles the size setting works without it
mainForm.add(table);
// adding this on outer feedback panel prevents displaying the error messages
//addVisibleOnWarningBehavior(getMainFeedbackPanel());
//addVisibleOnWarningBehavior(getTempFeedbackPanel());
}
// private void addVisibleOnWarningBehavior(Component c) {
// c.add(new VisibleEnableBehaviour() {
// @Override
// public boolean isVisible() {
// return PageCertDecisions.this.getFeedbackMessages().hasMessage(FeedbackMessage.WARNING);
// }
// });
// }
private List<IColumn<CertWorkItemDto, String>> initColumns() {
List<IColumn<CertWorkItemDto, String>> columns = new ArrayList<>();
IColumn column;
column = new CheckBoxHeaderColumn<>();
columns.add(column);
column = helper.createTypeColumn(OBJECT, this);
columns.add(column);
column = helper.createObjectNameColumn(this, "PageCertDecisions.table.objectName");
columns.add(column);
column = helper.createTypeColumn(TARGET, this);
columns.add(column);
column = helper.createTargetNameColumn(this, "PageCertDecisions.table.targetName");
columns.add(column);
column = helper.createDetailedInfoColumn(this);
columns.add(column);
column = helper.createConflictingNameColumn(this, "PageCertDecisions.table.conflictingTargetName");
columns.add(column);
if (WebComponentUtil.isAuthorized(AuthorizationConstants.AUTZ_UI_CERTIFICATION_ALL_URL,
AuthorizationConstants.AUTZ_UI_CERTIFICATION_CAMPAIGN_URL)) {
column = new LinkColumn<CertWorkItemDto>(
createStringResource("PageCertDecisions.table.campaignName"),
SearchingUtils.CAMPAIGN_NAME, CertWorkItemDto.F_CAMPAIGN_NAME) {
@Override
public void populateItem(Item<ICellPopulator<CertWorkItemDto>> item, String componentId, IModel<CertWorkItemDto> rowModel) {
super.populateItem(item, componentId, rowModel);
AccessCertificationCampaignType campaign = rowModel.getObject().getCampaign();
if (campaign != null && campaign.getDescription() != null) {
item.add(AttributeModifier.replace("title", campaign.getDescription()));
item.add(new TooltipBehavior());
}
}
@Override
public void onClick(AjaxRequestTarget target, IModel<CertWorkItemDto> rowModel) {
CertWorkItemDto dto = rowModel.getObject();
PageParameters parameters = new PageParameters();
parameters.add(OnePageParameterEncoder.PARAMETER, dto.getCampaignRef().getOid());
navigateToNext(PageCertCampaign.class, parameters);
}
};
} else {
column = new AbstractColumn<CertWorkItemDto, String>(createStringResource("PageCertDecisions.table.campaignName"), SearchingUtils.CAMPAIGN_NAME) {
@Override
public void populateItem(Item<ICellPopulator<CertWorkItemDto>> item, String componentId,
final IModel<CertWorkItemDto> rowModel) {
item.add(new Label(componentId, new AbstractReadOnlyModel<Object>() {
@Override
public Object getObject() {
return rowModel.getObject().getCampaignName();
}
}));
}
};
}
columns.add(column);
column = new AbstractColumn<CertWorkItemDto, String>(
createStringResource("PageCertDecisions.table.campaignStage")) {
@Override
public void populateItem(Item<ICellPopulator<CertWorkItemDto>> item, String componentId, final IModel<CertWorkItemDto> rowModel) {
item.add(new Label(componentId, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
CertWorkItemDto dto = rowModel.getObject();
return dto.getCampaignStageNumber() + "/" + dto.getCampaignStageCount();
}
}));
String stageName = rowModel.getObject().getCurrentStageName();
if (stageName != null) {
item.add(AttributeModifier.replace("title", stageName));
item.add(new TooltipBehavior());
}
}
};
columns.add(column);
column = new AbstractColumn<CertWorkItemDto, String>(
createStringResource("PageCertDecisions.table.escalation")) {
@Override
public void populateItem(Item<ICellPopulator<CertWorkItemDto>> item, String componentId, final IModel<CertWorkItemDto> rowModel) {
item.add(new Label(componentId, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
CertWorkItemDto dto = rowModel.getObject();
Integer n = dto.getEscalationLevelNumber();
return n != null ? String.valueOf(n) : null;
}
}));
String info = rowModel.getObject().getEscalationLevelInfo();
if (info != null) {
item.add(AttributeModifier.replace("title", info));
item.add(new TooltipBehavior());
}
}
};
columns.add(column);
column = new PropertyColumn<CertWorkItemDto, String>(
createStringResource("PageCertDecisions.table.requested"),
SearchingUtils.CURRENT_REVIEW_REQUESTED_TIMESTAMP, CertWorkItemDto.F_REVIEW_REQUESTED) {
@Override
public void populateItem(Item<ICellPopulator<CertWorkItemDto>> item, String componentId, IModel<CertWorkItemDto> rowModel) {
super.populateItem(item, componentId, rowModel);
CertWorkItemDto dto = rowModel.getObject();
Date started = dto.getStageStarted();
if (started != null) {
item.add(AttributeModifier.replace("title", WebComponentUtil.getLocalizedDate(started, DateLabelComponent.LONG_MEDIUM_STYLE)));
item.add(new TooltipBehavior());
}
}
};
columns.add(column);
column = new PropertyColumn<CertWorkItemDto, String>(createStringResource("PageCertDecisions.table.deadline"),
SearchingUtils.CURRENT_REVIEW_DEADLINE, CertWorkItemDto.F_DEADLINE_AS_STRING) {
@Override
public void populateItem(Item<ICellPopulator<CertWorkItemDto>> item, String componentId, final IModel<CertWorkItemDto> rowModel) {
super.populateItem(item, componentId, rowModel);
XMLGregorianCalendar deadline = rowModel.getObject().getCertCase().getCurrentStageDeadline();
if (deadline != null) {
item.add(AttributeModifier.replace("title", WebComponentUtil.formatDate(deadline)));
item.add(new TooltipBehavior());
}
}
};
columns.add(column);
final AvailableResponses availableResponses = new AvailableResponses(getPage());
final int responses = availableResponses.getResponseKeys().size();
column = new MultiButtonColumn<CertWorkItemDto>(new Model(), responses+1) {
@Override
public String getCaption(int id) {
return availableResponses.getCaption(id);
}
@Override
public boolean isButtonEnabled(int id, IModel<CertWorkItemDto> model) {
if (id < responses) {
return !decisionEquals(model, availableResponses.getResponseValues().get(id));
} else {
return false;
}
}
@Override
public boolean isButtonVisible(int id, IModel<CertWorkItemDto> model) {
if (id < responses) {
return true;
} else {
return !availableResponses.isAvailable(model.getObject().getResponse());
}
}
@Override
public String getButtonColorCssClass(int id) {
if (id < responses) {
return getDecisionButtonColor(getRowModel(), availableResponses.getResponseValues().get(id));
} else {
return BUTTON_COLOR_CLASS.DANGER.toString();
}
}
@Override
public void clickPerformed(int id, AjaxRequestTarget target,
IModel<CertWorkItemDto> model) {
if (id < responses) { // should be always the case
recordActionPerformed(target, model.getObject(), availableResponses.getResponseValues().get(id));
}
}
};
columns.add(column);
column = new DirectlyEditablePropertyColumn(
createStringResource("PageCertDecisions.table.comment"),
CertWorkItemDto.F_COMMENT) {
@Override
public void onBlur(AjaxRequestTarget target, IModel model) {
// TODO determine somehow if the model.comment was really changed
recordActionPerformed(target, (CertWorkItemDto) model.getObject(), null);
}
};
columns.add(column);
columns.add(new InlineMenuHeaderColumn(createInlineMenu(availableResponses)));
return columns;
}
private List<InlineMenuItem> createInlineMenu(AvailableResponses availableResponses) {
List<InlineMenuItem> items = new ArrayList<>();
if (availableResponses.isAvailable(ACCEPT)) {
items.add(createMenu("PageCertDecisions.menu.acceptSelected", ACCEPT));
}
if (availableResponses.isAvailable(REVOKE)) {
items.add(createMenu("PageCertDecisions.menu.revokeSelected", REVOKE));
}
if (availableResponses.isAvailable(REDUCE)) {
items.add(createMenu("PageCertDecisions.menu.reduceSelected", REDUCE));
}
if (availableResponses.isAvailable(NOT_DECIDED)) {
items.add(createMenu("PageCertDecisions.menu.notDecidedSelected", NOT_DECIDED));
}
if (availableResponses.isAvailable(NO_RESPONSE)) {
items.add(createMenu("PageCertDecisions.menu.noResponseSelected", NO_RESPONSE));
}
return items;
}
private InlineMenuItem createMenu(String titleKey, final AccessCertificationResponseType response) {
return new InlineMenuItem(createStringResource(titleKey), false,
new HeaderMenuAction(this) {
@Override
public void onClick(AjaxRequestTarget target) {
recordActionOnSelected(response, target);
}
});
}
private String getDecisionButtonColor(IModel<CertWorkItemDto> model, AccessCertificationResponseType response) {
if (decisionEquals(model, response)) {
return BUTTON_COLOR_CLASS.PRIMARY.toString();
} else {
return BUTTON_COLOR_CLASS.DEFAULT.toString();
}
}
private boolean decisionEquals(IModel<CertWorkItemDto> model, AccessCertificationResponseType response) {
return model.getObject().getResponse() == response;
}
private Table getDecisionsTable() {
return (Table) get(createComponentPath(ID_MAIN_FORM, ID_DECISIONS_TABLE));
}
//endregion
//region Actions
private void recordActionOnSelected(AccessCertificationResponseType response, AjaxRequestTarget target) {
List<CertWorkItemDto> workItemDtoList = WebComponentUtil.getSelectedData(getDecisionsTable());
if (workItemDtoList.isEmpty()) {
warn(getString("PageCertDecisions.message.noItemSelected"));
target.add(getFeedbackPanel());
return;
}
OperationResult result = new OperationResult(OPERATION_RECORD_ACTION_SELECTED);
Task task = createSimpleTask(OPERATION_RECORD_ACTION_SELECTED);
for (CertWorkItemDto workItemDto : workItemDtoList) {
OperationResult resultOne = result.createSubresult(OPERATION_RECORD_ACTION);
try {
getCertificationService().recordDecision(
workItemDto.getCampaignRef().getOid(),
workItemDto.getCaseId(), workItemDto.getWorkItemId(),
response, workItemDto.getComment(), task, resultOne);
} catch (Exception ex) {
resultOne.recordFatalError(ex);
} finally {
resultOne.computeStatusIfUnknown();
}
}
result.computeStatus();
if (!result.isSuccess()) {
showResult(result);
}
target.add(getFeedbackPanel());
target.add((Component) getDecisionsTable());
}
// if response is null this means keep the current one in workItemDto
private void recordActionPerformed(AjaxRequestTarget target, CertWorkItemDto workItemDto, AccessCertificationResponseType response) {
OperationResult result = new OperationResult(OPERATION_RECORD_ACTION);
try {
Task task = createSimpleTask(OPERATION_RECORD_ACTION);
// TODO work item ID
getCertificationService().recordDecision(
workItemDto.getCampaignRef().getOid(),
workItemDto.getCaseId(), workItemDto.getWorkItemId(),
response, workItemDto.getComment(), task, result);
} catch (Exception ex) {
result.recordFatalError(ex);
} finally {
result.computeStatusIfUnknown();
}
if (!result.isSuccess()) {
showResult(result);
}
target.add(getFeedbackPanel());
target.add((Component) getDecisionsTable());
}
private void searchFilterPerformed(AjaxRequestTarget target) {
ObjectQuery query = createCaseQuery();
Table panel = getDecisionsTable();
DataTable table = panel.getDataTable();
CertWorkItemDtoProvider provider = (CertWorkItemDtoProvider) table.getDataProvider();
provider.setQuery(query);
provider.setNotDecidedOnly(Boolean.TRUE.equals(showNotDecidedOnlyModel.getObject()));
table.setCurrentPage(0);
target.add(getFeedbackPanel());
target.add((Component) getDecisionsTable());
}
//endregion
// protected void dispatchToObjectDetailsPage(ObjectReferenceType objectRef) {
// if (objectRef == null) {
// return; // should not occur
// }
// QName type = objectRef.getType();
// PageParameters parameters = new PageParameters();
// parameters.add(OnePageParameterEncoder.PARAMETER, objectRef.getOid());
// if (RoleType.COMPLEX_TYPE.equals(type)) {
// setResponsePage(new PageRole(parameters, this));
// } else if (OrgType.COMPLEX_TYPE.equals(type)) {
// setResponsePage(new PageOrgUnit(parameters, this));
// } else if (UserType.COMPLEX_TYPE.equals(type)) {
// setResponsePage(new PageUser(parameters, this));
// } else if (ResourceType.COMPLEX_TYPE.equals(type)) {
// setResponsePage(new PageResource(parameters, this));
// } else {
// // nothing to do
// }
// }
private static class SearchFragment extends Fragment {
public SearchFragment(String id, String markupId, MarkupContainer markupProvider,
IModel<Boolean> 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<Boolean> model = (IModel<Boolean>) getDefaultModel();
CheckBox showNotDecidedOnlyBox = new CheckBox(ID_SHOW_NOT_DECIDED_ONLY, model);
showNotDecidedOnlyBox.add(createFilterAjaxBehaviour());
searchForm.add(showNotDecidedOnlyBox);
}
private AjaxFormComponentUpdatingBehavior createFilterAjaxBehaviour() {
return new AjaxFormComponentUpdatingBehavior("change") {
@Override
protected void onUpdate(AjaxRequestTarget target) {
PageCertDecisions page = (PageCertDecisions) getPage();
page.searchFilterPerformed(target);
}
};
}
}
}