Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.stream.Collectors;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.gui.impl.page.admin.certification.helpers.CertMiscUtil;
import com.evolveum.midpoint.web.page.admin.services.*;

import org.apache.commons.collections4.CollectionUtils;
Expand Down Expand Up @@ -157,14 +158,16 @@ protected String load() {
@Override
protected String load() {
try {
AccessCertificationService acs = getPageBase().getCertificationService();
Task task = getPageBase().createSimpleTask(OPERATION_LOAD_CERT_WORK_ITEM_COUNT);
OperationResult result = task.getResult();
int openCertWorkItems = acs.countOpenWorkItems(getPrismContext().queryFactory().createQuery(), true, null, task, result);
if (openCertWorkItems == 0) {
// Use method that respects collectDecisionsFromAllReviewers setting
long openCertItems = CertMiscUtil.countOpenCertItems(
(String) null, // all campaigns
getPageBase().getPrincipal(),
true, // notDecidedOnly
getPageBase());
if (openCertItems == 0) {
return null;
}
return Integer.toString(openCertWorkItems);
return Long.toString(openCertItems);
} catch (Exception e) {
LoggingUtils.logExceptionAsWarning(LOGGER, "Couldn't load certification work item count", e);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@

import com.evolveum.midpoint.certification.api.OutcomeUtils;
import com.evolveum.midpoint.gui.api.page.PageBase;
import com.evolveum.midpoint.gui.impl.page.admin.certification.helpers.CertMiscUtil;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.expression.VariablesMap;
import com.evolveum.midpoint.util.DisplayableValue;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationResponseType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationWorkItemType;

Expand All @@ -39,9 +43,22 @@ public ObjectFilter createFilter(Class type, PageBase pageBase, VariablesMap var
if (AccessCertificationResponseType.NO_RESPONSE.equals(response) &&
AccessCertificationWorkItemType.class.equals(type)) {
//work items without response have null outcome
return PrismContext.get().queryFor(type)
ObjectFilter noOutcomeFilter = PrismContext.get().queryFor(type)
.item(getPath()).isNull()
.buildFilter();

if (!CertMiscUtil.isCollectDecisionsFromAllReviewers(pageBase)) {
// Also filter by parent case not having a response yet
ObjectFilter caseNotDecidedFilter = PrismContext.get().queryFor(type)
.exists(PrismConstants.T_PARENT)
.block()
.item(AccessCertificationCaseType.F_CURRENT_STAGE_OUTCOME)
.eq(SchemaConstants.MODEL_CERTIFICATION_OUTCOME_NO_RESPONSE)
.endBlock()
.buildFilter();
return PrismContext.get().queryFactory().createAnd(noOutcomeFilter, caseNotDecidedFilter);
}
return noOutcomeFilter;
}
return PrismContext.get().queryFor(type)
.item(getPath()).eq(OutcomeUtils.toUri(response)).buildFilter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ protected boolean isAuthorizedForCampaignActions() {

@Override
protected LoadableDetachableModel<List<ProgressBar>> createCampaignProgressModel() {
return CertMiscUtil.createCampaignWorkItemsProgressBarModel(getCampaign(), getPrincipalOid(), getPageBase());
return CertMiscUtil.createCampaignProgressBarModel(getCampaign(), getPrincipalOid(), getPageBase());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommonException;
Expand Down Expand Up @@ -96,20 +97,15 @@ protected List<CampaignSummary> load() {
for (PrismObject<AccessCertificationCampaignType> campaignObj : campaigns) {
AccessCertificationCampaignType campaign = campaignObj.asObjectable();

ObjectQuery workItems = CertCampaignTypeUtil.createWorkItemsForCampaignQuery(campaign.getOid());
// Use method that respects collectDecisionsFromAllReviewers setting
MidPointPrincipal principal = page.getPrincipal();
long openNotDecided = CertMiscUtil.countOpenCertItems(
campaign.getOid(), principal, true, page);
long allOpen = CertMiscUtil.countOpenCertItems(
campaign.getOid(), principal, false, page);
long openDecided = allOpen - openNotDecided;

try {
int openNotDecided = page.getCertificationService().countOpenWorkItems(workItems, true,
false, null, task, task.getResult());
int allOpen = page.getCertificationService().countOpenWorkItems(workItems, false,
false, null, task, task.getResult());
int openDecided = allOpen - openNotDecided;

summary.add(new CampaignSummary(campaign, openDecided, openNotDecided));
} catch (CommonException ex) {
LOGGER.error("Couldn't load certification campaign work items count for campaign {}: {}",
campaign.getName(), ex.getMessage(), ex);
}
summary.add(new CampaignSummary(campaign, openDecided, openNotDecided));
}

LOGGER.debug("Loaded {} active campaigns summary", summary.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ private long getNotDecidedItemsCount(PrismObject<FocusType> reviewer) {
if (isInReviewStage()) {
String campaignOid = model.getObjectType().getOid();
MidPointPrincipal principal = MidPointPrincipal.create(reviewer.asObjectable());
// Use method that respects collectDecisionsFromAllReviewers setting
return CertMiscUtil.countOpenCertItems(Collections.singletonList(campaignOid), principal,
true, getPageBase());
} else if (isReviewStageDone()) {
Expand All @@ -310,6 +311,7 @@ private long getAllItemsCount(PrismObject<FocusType> reviewer) {
if (isInReviewStage()) {
String campaignOid = model.getObjectType().getOid();
MidPointPrincipal principal = MidPointPrincipal.create(reviewer.asObjectable());
// Use method that respects collectDecisionsFromAllReviewers setting
return CertMiscUtil.countOpenCertItems(Collections.singletonList(campaignOid), principal,
false, getPageBase());
} else if (isReviewStageDone()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.evolveum.midpoint.model.api.authentication.CompiledObjectCollectionView;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.impl.PrismReferenceValueImpl;
Expand All @@ -50,6 +51,7 @@
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.security.api.OtherPrivilegesLimitations;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
Expand Down Expand Up @@ -318,7 +320,7 @@ private static ChartDataset createDataSet(List<String> campaignOids, MidPointPri
return dataset;
}

public static long countOpenCertItems(List<String> campaignOids, MidPointPrincipal principal, boolean notDecidedOnly,
private static long countOpenWorkItemsForCampaigns(List<String> campaignOids, MidPointPrincipal principal, boolean notDecidedOnly,
PageBase pageBase) {
long count = 0;

Expand Down Expand Up @@ -902,4 +904,212 @@ private static GuiObjectListViewType getCollectionViewConfigurationFromCampaignD
AccessCertificationDefinitionType definition = definitionObj.asObjectable();
return definition.getView();
}

/**
* Returns whether decisions should be collected from all reviewers.
* If true (default), progress is calculated based on WorkItems.
* If false, progress is calculated based on Cases.
*/
public static boolean isCollectDecisionsFromAllReviewers(PageBase pageBase) {
AccessCertificationConfigurationType config = getCertificationConfiguration(pageBase);
if (config == null || config.isCollectDecisionsFromAllReviewers() == null) {
return true; // default: true
}
return config.isCollectDecisionsFromAllReviewers();
}

/**
* Returns the certification configuration from system configuration.
*/
public static AccessCertificationConfigurationType getCertificationConfiguration(PageBase pageBase) {
try {
OperationResult result = new OperationResult(OPERATION_LOAD_CERTIFICATION_CONFIG);
return pageBase.getModelInteractionService().getCertificationConfiguration(result);
} catch (Exception e) {
LOGGER.error("Couldn't load certification configuration from system configuration", e);
return null;
}
}

/**
* Counts open certification items based on the collectDecisionsFromAllReviewers setting.
* When true, counts work items. When false, counts cases.
*
* @param campaignOid the campaign OID (can be null for all campaigns)
* @param principal the principal (can be null for all reviewers; includes deputy support)
* @param notDecidedOnly if true, count only not-decided items
* @param pageBase the page base
* @return the count of open items
*/
public static long countOpenCertItems(String campaignOid, MidPointPrincipal principal, boolean notDecidedOnly, PageBase pageBase) {
if (isCollectDecisionsFromAllReviewers(pageBase)) {
return countOpenWorkItemsInternal(campaignOid, principal, notDecidedOnly, pageBase);
} else {
return countOpenCasesInternal(campaignOid, principal, notDecidedOnly, pageBase);
}
}

/**
* Counts open certification items for multiple campaigns based on the collectDecisionsFromAllReviewers setting.
*/
public static long countOpenCertItems(List<String> campaignOids, MidPointPrincipal principal, boolean notDecidedOnly, PageBase pageBase) {
if (isCollectDecisionsFromAllReviewers(pageBase)) {
return countOpenWorkItemsForCampaigns(campaignOids, principal, notDecidedOnly, pageBase);
} else {
return countOpenCasesForCampaigns(campaignOids, principal, notDecidedOnly, pageBase);
}
}

/**
* Internal method to count open work items for a single campaign.
*/
private static long countOpenWorkItemsInternal(String campaignOid, MidPointPrincipal principal, boolean notDecidedOnly, PageBase pageBase) {
Task task = pageBase.createSimpleTask("countCertificationWorkItems");
OperationResult result = task.getResult();
try {
ObjectQuery baseQuery;
if (campaignOid != null) {
baseQuery = PrismContext.get().queryFor(AccessCertificationWorkItemType.class)
.exists(PrismConstants.T_PARENT)
.ownerId(campaignOid)
.build();
} else {
baseQuery = PrismContext.get().queryFor(AccessCertificationWorkItemType.class)
.build();
}
boolean collectAll = isCollectDecisionsFromAllReviewers(pageBase);
ObjectQuery query = QueryUtils.createQueryForOpenWorkItems(baseQuery, principal, notDecidedOnly, collectAll);
if (query == null) {
return 0;
}
return pageBase.getModelService()
.countContainers(AccessCertificationWorkItemType.class, query, null, task, result);
} catch (Exception ex) {
LOGGER.error("Couldn't count certification work items", ex);
pageBase.showResult(result);
}
return 0;
}

/**
* Counts open cases for a single campaign (or all campaigns if campaignOid is null).
* When notDecidedOnly is true, counts only cases with currentStageOutcome = NO_RESPONSE.
* Supports deputy/delegation via principal-based filtering.
*
* @param campaignOid the campaign OID, or null to count across all campaigns
*/
private static long countOpenCasesInternal(String campaignOid, MidPointPrincipal principal, boolean notDecidedOnly, PageBase pageBase) {
Task task = pageBase.createSimpleTask("countCertificationCases");
OperationResult result = task.getResult();
try {
var q = PrismContext.get().queryFor(AccessCertificationCaseType.class);

S_FilterExit baseFilter;
if (campaignOid != null) {
baseFilter = q.ownerId(campaignOid);
} else {
baseFilter = q.all();
}

if (notDecidedOnly) {
baseFilter = baseFilter.and()
.item(AccessCertificationCaseType.F_CURRENT_STAGE_OUTCOME)
.eq(OutcomeUtils.toUri(NO_RESPONSE));
}

if (principal != null) {
baseFilter = baseFilter.and()
.exists(AccessCertificationCaseType.F_WORK_ITEM)
.block()
.item(AccessCertificationWorkItemType.F_CLOSE_TIMESTAMP).isNull()
.and()
.item(AbstractWorkItemType.F_ASSIGNEE_REF)
.ref(QueryUtils.getPotentialAssigneesForUser(principal, // includes deputies
OtherPrivilegesLimitations.Type.ACCESS_CERTIFICATION))
.endBlock();
} else {
baseFilter = baseFilter.and()
.exists(AccessCertificationCaseType.F_WORK_ITEM)
.block()
.item(AccessCertificationWorkItemType.F_CLOSE_TIMESTAMP).isNull()
.endBlock();
}

ObjectQuery query = baseFilter.build();

return pageBase.getModelService()
.countContainers(AccessCertificationCaseType.class, query, null, task, result);
} catch (Exception ex) {
LOGGER.error("Couldn't count certification cases", ex);
pageBase.showResult(result);
}
return 0;
}

/**
* Counts open cases for the specified campaigns. Returns 0 if campaignOids is null or empty.
* Supports deputy/delegation via principal-based filtering.
*
* @param campaignOids the campaign OIDs; returns 0 if null or empty
*/
private static long countOpenCasesForCampaigns(List<String> campaignOids, MidPointPrincipal principal, boolean notDecidedOnly, PageBase pageBase) {
if (campaignOids == null || campaignOids.isEmpty()) {
return 0;
}

Task task = pageBase.createSimpleTask("countCertificationCases");
OperationResult result = task.getResult();
try {
String[] oids = campaignOids.toArray(new String[0]);

S_FilterExit baseFilter = PrismContext.get().queryFor(AccessCertificationCaseType.class)
.ownerId(oids);

if (notDecidedOnly) {
baseFilter = baseFilter.and()
.item(AccessCertificationCaseType.F_CURRENT_STAGE_OUTCOME)
.eq(OutcomeUtils.toUri(NO_RESPONSE));
}

if (principal != null) {
baseFilter = baseFilter.and()
.exists(AccessCertificationCaseType.F_WORK_ITEM)
.block()
.item(AccessCertificationWorkItemType.F_CLOSE_TIMESTAMP).isNull()
.and()
.item(AbstractWorkItemType.F_ASSIGNEE_REF)
.ref(QueryUtils.getPotentialAssigneesForUser(principal, // includes deputies
OtherPrivilegesLimitations.Type.ACCESS_CERTIFICATION))
.endBlock();
} else {
baseFilter = baseFilter.and()
.exists(AccessCertificationCaseType.F_WORK_ITEM)
.block()
.item(AccessCertificationWorkItemType.F_CLOSE_TIMESTAMP).isNull()
.endBlock();
}

ObjectQuery query = baseFilter.build();

return pageBase.getModelService()
.countContainers(AccessCertificationCaseType.class, query, null, task, result);
} catch (Exception ex) {
LOGGER.error("Couldn't count certification cases for campaigns", ex);
pageBase.showResult(result);
}
return 0;
}

/**
* Creates a progress bar model based on the collectDecisionsFromAllReviewers setting.
* When true, uses work item-based progress. When false, uses case-based progress.
*/
public static LoadableDetachableModel<List<ProgressBar>> createCampaignProgressBarModel(
AccessCertificationCampaignType campaign, String principalOid, PageBase pageBase) {
if (isCollectDecisionsFromAllReviewers(pageBase)) {
return createCampaignWorkItemsProgressBarModel(campaign, principalOid, pageBase);
} else {
return createCampaignCasesProgressBarModel(campaign, principalOid, pageBase);
}
}
}
Loading