/* * 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.web.page.self; import static com.evolveum.midpoint.prism.PrismConstants.T_PARENT; import static com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType.F_CREATE_TIMESTAMP; import java.util.*; import com.evolveum.midpoint.gui.api.PredefinedDashboardWidgetId; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.schema.util.AdminGuiConfigTypeUtil; import com.evolveum.midpoint.web.application.Url; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.Validate; import org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.Session; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.springframework.security.core.Authentication; import com.evolveum.midpoint.gui.api.GuiStyleConstants; 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.PrismContainer; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.task.api.Task; 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.SecurityContextAwareCallable; import com.evolveum.midpoint.web.component.assignment.AssignmentEditorDtoType; import com.evolveum.midpoint.web.component.breadcrumbs.Breadcrumb; import com.evolveum.midpoint.web.component.util.CallableResult; import com.evolveum.midpoint.web.component.util.ListDataProvider; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import com.evolveum.midpoint.web.component.wf.WorkItemsPanel; import com.evolveum.midpoint.web.page.admin.home.component.AsyncDashboardPanel; import com.evolveum.midpoint.web.page.admin.home.component.MyAccountsPanel; import com.evolveum.midpoint.web.page.admin.home.component.MyAssignmentsPanel; import com.evolveum.midpoint.web.page.admin.home.dto.AccountCallableResult; import com.evolveum.midpoint.web.page.admin.home.dto.AssignmentItemDto; import com.evolveum.midpoint.web.page.admin.home.dto.SimpleAccountDto; import com.evolveum.midpoint.web.page.admin.workflow.ProcessInstancesPanel; import com.evolveum.midpoint.web.page.admin.workflow.dto.ProcessInstanceDto; import com.evolveum.midpoint.web.page.admin.workflow.dto.ProcessInstanceDtoProvider; import com.evolveum.midpoint.web.page.admin.workflow.dto.WorkItemDto; import com.evolveum.midpoint.web.page.self.component.DashboardSearchPanel; import com.evolveum.midpoint.web.page.self.component.LinksPanel; import com.evolveum.midpoint.web.security.SecurityUtils; /** * @author Viliam Repan (lazyman) * @author Kate Honchar */ @PageDescriptor( urls = { @Url(mountUrl = "/self", matchUrlForSecurity = "/self"), @Url(mountUrl = "/self/dashboard") }, action = { @AuthorizationAction(actionUri = PageSelf.AUTH_SELF_ALL_URI, label = PageSelf.AUTH_SELF_ALL_LABEL, description = PageSelf.AUTH_SELF_ALL_DESCRIPTION), @AuthorizationAction(actionUri = AuthorizationConstants.AUTZ_UI_SELF_DASHBOARD_URL, label = "PageSelfDashboard.auth.dashboard.label", description = "PageSelfDashboard.auth.dashboard.description") }) public class PageSelfDashboard extends PageSelf { private static final Trace LOGGER = TraceManager.getTrace(PageSelfDashboard.class); private static final String ID_LINKS_PANEL = "linksPanel"; private static final String ID_WORK_ITEMS_PANEL = "workItemsPanel"; private static final String ID_SEARCH_PANEL = "searchPanel"; private static final String ID_REQUESTS_PANEL = "requestPanel"; private static final String ID_ACCOUNTS = "accounts"; private static final String ID_ASSIGNMENTS = "assignments"; private static final String DOT_CLASS = PageSelfDashboard.class.getName() + "."; private static final String OPERATION_LOAD_WORK_ITEMS = DOT_CLASS + "loadWorkItems"; private static final String OPERATION_LOAD_REQUESTS = DOT_CLASS + "loadRequests"; private static final String OPERATION_LOAD_ACCOUNTS = DOT_CLASS + "loadAccounts"; private static final String OPERATION_LOAD_ASSIGNMENTS = DOT_CLASS + "loadAssignments"; private static final String OPERATION_LOAD_USER = DOT_CLASS + "loadUser"; private static final String OPERATION_GET_SYSTEM_CONFIG = DOT_CLASS + "getSystemConfiguration"; private static final int MAX_WORK_ITEMS = 1000; private static final int MAX_REQUESTS = 1000; private final Model<PrismObject<UserType>> principalModel = new Model<PrismObject<UserType>>(); private AdminGuiConfigurationType adminGuiConfig; public PageSelfDashboard() { adminGuiConfig = getPrincipal().getAdminGuiConfiguration(); principalModel.setObject(loadUser()); setTimeZone(PageSelfDashboard.this); initLayout(); } private transient Application application; @Override protected void createBreadcrumb() { super.createBreadcrumb(); Breadcrumb bc = getLastBreadcrumb(); bc.setIcon(new Model("fa fa-dashboard")); } private void initLayout(){ DashboardSearchPanel dashboardSearchPanel = new DashboardSearchPanel(ID_SEARCH_PANEL, null); List<String> searchPanelActions = Arrays.asList(AuthorizationConstants.AUTZ_UI_USERS_ALL_URL, AuthorizationConstants.AUTZ_UI_USERS_URL, AuthorizationConstants.AUTZ_UI_RESOURCES_ALL_URL, AuthorizationConstants.AUTZ_UI_RESOURCES_URL, AuthorizationConstants.AUTZ_UI_TASKS_ALL_URL, AuthorizationConstants.AUTZ_UI_TASKS_URL); dashboardSearchPanel.add(new VisibleEnableBehaviour(){ private static final long serialVersionUID = 1L; @Override public boolean isVisible(){ UserInterfaceElementVisibilityType visibilityType = getComponentVisibility(PredefinedDashboardWidgetId.SEARCH); return WebComponentUtil.getElementVisibility(visibilityType, searchPanelActions); } }); add(dashboardSearchPanel); LinksPanel linksPanel = new LinksPanel(ID_LINKS_PANEL, Model.ofList(loadLinksList())); linksPanel.add(new VisibleEnableBehaviour(){ private static final long serialVersionUID = 1L; @Override public boolean isVisible(){ UserInterfaceElementVisibilityType visibilityType = getComponentVisibility(PredefinedDashboardWidgetId.SHORTCUTS); return WebComponentUtil.getElementVisibility(visibilityType); } }); add(linksPanel); // TODO is this correct? [med] application = getApplication(); final Session session = Session.get(); AsyncDashboardPanel<Object, List<WorkItemDto>> workItemsPanel = new AsyncDashboardPanel<Object, List<WorkItemDto>>( ID_WORK_ITEMS_PANEL, createStringResource("PageSelfDashboard.workItems"), GuiStyleConstants.CLASS_OBJECT_WORK_ITEM_ICON, GuiStyleConstants.CLASS_OBJECT_WORK_ITEM_BOX_CSS_CLASSES, true) { private static final long serialVersionUID = 1L; @Override protected SecurityContextAwareCallable<CallableResult<List<WorkItemDto>>> createCallable( Authentication auth, IModel callableParameterModel) { return new SecurityContextAwareCallable<CallableResult<List<WorkItemDto>>>( getSecurityEnforcer(), auth) { private static final long serialVersionUID = 1L; @Override public CallableResult<List<WorkItemDto>> callWithContextPrepared() throws Exception { setupContext(application, session); // TODO is this correct? [med] return loadWorkItems(); } }; } @Override protected Component getMainComponent(String markupId) { ISortableDataProvider provider = new ListDataProvider(this, new PropertyModel<List<WorkItemDto>>(getModel(), CallableResult.F_VALUE)); return new WorkItemsPanel(markupId, provider, null, 10, WorkItemsPanel.View.DASHBOARD); } }; workItemsPanel.add(new VisibleEnableBehaviour() { @Override public boolean isVisible() { UserInterfaceElementVisibilityType visibilityType = getComponentVisibility(PredefinedDashboardWidgetId.MY_WORKITEMS); return getWorkflowManager().isEnabled() && WebComponentUtil.getElementVisibility(visibilityType); } }); add(workItemsPanel); AsyncDashboardPanel<Object, List<ProcessInstanceDto>> myRequestsPanel = new AsyncDashboardPanel<Object, List<ProcessInstanceDto>>(ID_REQUESTS_PANEL, createStringResource("PageSelfDashboard.myRequests"), GuiStyleConstants.CLASS_SHADOW_ICON_REQUEST, GuiStyleConstants.CLASS_OBJECT_SERVICE_BOX_CSS_CLASSES, true) { private static final long serialVersionUID = 1L; @Override protected SecurityContextAwareCallable<CallableResult<List<ProcessInstanceDto>>> createCallable( Authentication auth, IModel callableParameterModel) { return new SecurityContextAwareCallable<CallableResult<List<ProcessInstanceDto>>>( getSecurityEnforcer(), auth) { private static final long serialVersionUID = 1L; @Override public CallableResult<List<ProcessInstanceDto>> callWithContextPrepared() throws Exception { setupContext(application, session); return loadMyRequests(); } }; } @Override protected Component getMainComponent(String markupId) { ISortableDataProvider provider = new ListDataProvider(this, new PropertyModel<List<ProcessInstanceDto>>(getModel(), CallableResult.F_VALUE)); return new ProcessInstancesPanel(markupId, provider, null, 10, ProcessInstancesPanel.View.DASHBOARD, null); } }; myRequestsPanel.add(new VisibleEnableBehaviour() { private static final long serialVersionUID = 1L; @Override public boolean isVisible() { UserInterfaceElementVisibilityType visibilityType = getComponentVisibility(PredefinedDashboardWidgetId.MY_REQUESTS); return getWorkflowManager().isEnabled() && WebComponentUtil.getElementVisibility(visibilityType); } }); add(myRequestsPanel); initMyAccounts(); initAssignments(); } private CallableResult<List<WorkItemDto>> loadWorkItems() { LOGGER.debug("Loading work items."); AccountCallableResult callableResult = new AccountCallableResult(); List<WorkItemDto> list = new ArrayList<>(); callableResult.setValue(list); if (!getWorkflowManager().isEnabled()) { return callableResult; } PrismObject<UserType> user = principalModel.getObject(); if (user == null) { return callableResult; } Task task = createSimpleTask(OPERATION_LOAD_WORK_ITEMS); OperationResult result = task.getResult(); callableResult.setResult(result); try { ObjectQuery query = QueryBuilder.queryFor(WorkItemType.class, getPrismContext()) .item(WorkItemType.F_ASSIGNEE_REF).ref(user.getOid()) .desc(F_CREATE_TIMESTAMP) .build(); Collection<SelectorOptions<GetOperationOptions>> options = GetOperationOptions.resolveItemsNamed( new ItemPath(T_PARENT, WfContextType.F_OBJECT_REF), new ItemPath(T_PARENT, WfContextType.F_TARGET_REF)); List<WorkItemType> workItems = getModelService().searchContainers(WorkItemType.class, query, options, task, result); for (WorkItemType workItem : workItems) { list.add(new WorkItemDto(workItem)); } } catch (Exception e) { result.recordFatalError("Couldn't get list of work items.", e); } result.recordSuccessIfUnknown(); result.recomputeStatus(); LOGGER.debug("Finished work items loading."); return callableResult; } private CallableResult<List<ProcessInstanceDto>> loadMyRequests() { LOGGER.debug("Loading requests."); AccountCallableResult<List<ProcessInstanceDto>> callableResult = new AccountCallableResult<>(); List<ProcessInstanceDto> list = new ArrayList<ProcessInstanceDto>(); callableResult.setValue(list); if (!getWorkflowManager().isEnabled()) { return callableResult; } ProcessInstanceDtoProvider provider = new ProcessInstanceDtoProvider(this, true, false); provider.iterator(0, Integer.MAX_VALUE); callableResult.setValue(provider.getAvailableData()); LOGGER.debug("Finished requests loading."); return callableResult; } private PrismObject<UserType> loadUser() { MidPointPrincipal principal = SecurityUtils.getPrincipalUser(); Validate.notNull(principal, "No principal"); if (principal.getOid() == null) { throw new IllegalArgumentException("No OID in principal: "+principal); } Task task = createSimpleTask(OPERATION_LOAD_USER); OperationResult result = task.getResult(); PrismObject<UserType> user = WebModelServiceUtils.loadObject(UserType.class, principal.getOid(), PageSelfDashboard.this, task, result); result.computeStatus(); if (!WebComponentUtil.isSuccessOrHandledError(result)) { showResult(result); } return user; } private List<RichHyperlinkType> loadLinksList() { PrismObject<UserType> user = principalModel.getObject(); if (user == null) { return new ArrayList<RichHyperlinkType>(); } else { return ((PageBase)getPage()).loadAdminGuiConfiguration().getUserDashboardLink(); } } private void initMyAccounts() { AsyncDashboardPanel<Object, List<SimpleAccountDto>> accounts = new AsyncDashboardPanel<Object, List<SimpleAccountDto>>(ID_ACCOUNTS, createStringResource("PageDashboard.accounts"), GuiStyleConstants.CLASS_SHADOW_ICON_ACCOUNT, GuiStyleConstants.CLASS_OBJECT_SHADOW_BOX_CSS_CLASSES, true) { private static final long serialVersionUID = 1L; @Override protected SecurityContextAwareCallable<CallableResult<List<SimpleAccountDto>>> createCallable( Authentication auth, IModel<Object> callableParameterModel) { return new SecurityContextAwareCallable<CallableResult<List<SimpleAccountDto>>>( getSecurityEnforcer(), auth) { @Override public AccountCallableResult<List<SimpleAccountDto>> callWithContextPrepared() throws Exception { return loadAccounts(); } }; } @Override protected Component getMainComponent(String markupId) { return new MyAccountsPanel(markupId, new PropertyModel<List<SimpleAccountDto>>(getModel(), CallableResult.F_VALUE)); } @Override protected void onPostSuccess(AjaxRequestTarget target) { showFetchResult(); super.onPostSuccess(target); } @Override protected void onUpdateError(AjaxRequestTarget target, Exception ex) { showFetchResult(); super.onUpdateError(target, ex); } private void showFetchResult() { AccountCallableResult<List<SimpleAccountDto>> result = (AccountCallableResult<List<SimpleAccountDto>>) getModel().getObject(); PageBase page = (PageBase) getPage(); for (OperationResult res : result.getFetchResults()) { if (!WebComponentUtil.isSuccessOrHandledError(res)) { page.showResult(res); } } } }; accounts.add(new VisibleEnableBehaviour(){ private static final long serialVersionUID = 1L; @Override public boolean isVisible() { UserInterfaceElementVisibilityType visibilityType = getComponentVisibility(PredefinedDashboardWidgetId.MY_ACCOUNTS); return WebComponentUtil.getElementVisibility(visibilityType); } }); add(accounts); } private AccountCallableResult<List<SimpleAccountDto>> loadAccounts() throws Exception { LOGGER.debug("Loading accounts."); AccountCallableResult callableResult = new AccountCallableResult(); List<SimpleAccountDto> list = new ArrayList<SimpleAccountDto>(); callableResult.setValue(list); PrismObject<UserType> user = principalModel.getObject(); if (user == null) { return callableResult; } Task task = createSimpleTask(OPERATION_LOAD_ACCOUNTS); OperationResult result = task.getResult(); callableResult.setResult(result); GetOperationOptions getOpts = GetOperationOptions.createResolve(); getOpts.setNoFetch(Boolean.TRUE); Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(ShadowType.F_RESOURCE, getOpts); SelectorOptions<GetOperationOptions> resolveNamesOptions = new SelectorOptions(GetOperationOptions.createResolveNames()); resolveNamesOptions.getOptions().setNoFetch(Boolean.TRUE); options.add(resolveNamesOptions); List<ObjectReferenceType> references = user.asObjectable().getLinkRef(); for (ObjectReferenceType reference : references) { PrismObject<ShadowType> account = WebModelServiceUtils.loadObject(ShadowType.class, reference.getOid(), options, this, task, result); if (account == null) { continue; } ShadowType accountType = account.asObjectable(); OperationResultType fetchResult = accountType.getFetchResult(); if (fetchResult != null) { callableResult.getFetchResults().add(OperationResult.createOperationResult(fetchResult)); } ResourceType resource = accountType.getResource(); String resourceName = WebComponentUtil.getName(resource); list.add(new SimpleAccountDto(WebComponentUtil.getOrigStringFromPoly(accountType.getName()), resourceName)); } result.recordSuccessIfUnknown(); result.recomputeStatus(); LOGGER.debug("Finished accounts loading."); return callableResult; } private void initAssignments() { AsyncDashboardPanel<Object, List<AssignmentItemDto>> assignedOrgUnits = new AsyncDashboardPanel<Object, List<AssignmentItemDto>>(ID_ASSIGNMENTS, createStringResource("PageDashboard.assignments"), GuiStyleConstants.CLASS_ICON_ASSIGNMENTS, GuiStyleConstants.CLASS_OBJECT_ROLE_BOX_CSS_CLASSES, true) { private static final long serialVersionUID = 1L; @Override protected SecurityContextAwareCallable<CallableResult<List<AssignmentItemDto>>> createCallable( Authentication auth, IModel callableParameterModel) { return new SecurityContextAwareCallable<CallableResult<List<AssignmentItemDto>>>( getSecurityEnforcer(), auth) { @Override public CallableResult<List<AssignmentItemDto>> callWithContextPrepared() throws Exception { return loadAssignments(); } }; } @Override protected Component getMainComponent(String markupId) { return new MyAssignmentsPanel(markupId, new PropertyModel<List<AssignmentItemDto>>(getModel(), CallableResult.F_VALUE)); } }; assignedOrgUnits.add(new VisibleEnableBehaviour(){ private static final long serialVersionUID = 1L; @Override public boolean isVisible() { UserInterfaceElementVisibilityType visibilityType = getComponentVisibility(PredefinedDashboardWidgetId.MY_ASSIGNMENTS); return WebComponentUtil.getElementVisibility(visibilityType); } }); add(assignedOrgUnits); } private CallableResult<List<AssignmentItemDto>> loadAssignments() throws Exception { LOGGER.debug("Loading assignments."); CallableResult callableResult = new CallableResult(); List<AssignmentItemDto> list = new ArrayList<AssignmentItemDto>(); callableResult.setValue(list); PrismObject<UserType> user = principalModel.getObject(); if (user == null || user.findContainer(UserType.F_ASSIGNMENT) == null) { return callableResult; } Task task = createSimpleTask(OPERATION_LOAD_ASSIGNMENTS); OperationResult result = task.getResult(); callableResult.setResult(result); PrismContainer assignments = user.findContainer(UserType.F_ASSIGNMENT); List<PrismContainerValue> values = assignments.getValues(); for (PrismContainerValue assignment : values) { AssignmentItemDto item = createAssignmentItem(user, assignment, task, result); if (item != null) { list.add(item); } } result.recordSuccessIfUnknown(); result.recomputeStatus(); Collections.sort(list); LOGGER.debug("Finished assignments loading."); return callableResult; } private AssignmentItemDto createAssignmentItem(PrismObject<UserType> user, PrismContainerValue<AssignmentType> assignment, Task task, OperationResult result) { ActivationType activation = assignment.asContainerable().getActivation(); if (activation != null && activation.getAdministrativeStatus() != null && !activation.getAdministrativeStatus().equals(ActivationStatusType.ENABLED)) { return null; } PrismReference targetRef = assignment.findReference(AssignmentType.F_TARGET_REF); if (targetRef == null || targetRef.isEmpty()) { // account construction PrismContainer construction = assignment.findContainer(AssignmentType.F_CONSTRUCTION); String name = null; if (construction.getValue().asContainerable() != null && !construction.isEmpty()) { ConstructionType constr = (ConstructionType) construction.getValue().asContainerable(); if (constr.getResourceRef() != null) { ObjectReferenceType resourceRef = constr.getResourceRef(); PrismObject resource = WebModelServiceUtils.loadObject(ResourceType.class, resourceRef.getOid(), this, task, result); name = WebComponentUtil.getName(resource); } } return new AssignmentItemDto(AssignmentEditorDtoType.CONSTRUCTION, name, null, null); } PrismReferenceValue refValue = targetRef.getValue(); PrismObject value = refValue.getObject(); if (value == null) { // resolve reference value = WebModelServiceUtils.loadObject(ObjectType.class, refValue.getOid(), this, task, result); } if (value == null) { // we couldn't resolve assignment details return new AssignmentItemDto(null, null, null, null); } String name = WebComponentUtil.getDisplayNameOrName(value); AssignmentEditorDtoType type = AssignmentEditorDtoType.getType(value.getCompileTimeClass()); String relation = refValue.getRelation() != null ? refValue.getRelation().getLocalPart() : null; String description = null; if (OrgType.class.isAssignableFrom(value.getCompileTimeClass())) { description = (String) value.getPropertyRealValue(OrgType.F_IDENTIFIER, String.class); } return new AssignmentItemDto(type, name, description, relation); } private UserInterfaceElementVisibilityType getComponentVisibility(PredefinedDashboardWidgetId componentId){ if (adminGuiConfig == null || adminGuiConfig.getUserDashboard() == null) { return UserInterfaceElementVisibilityType.AUTOMATIC; } List<DashboardWidgetType> widgetsList = adminGuiConfig.getUserDashboard().getWidget(); if (widgetsList == null || widgetsList.size() == 0){ return UserInterfaceElementVisibilityType.VACANT; } DashboardWidgetType widget = AdminGuiConfigTypeUtil.findWidget(adminGuiConfig.getUserDashboard(), componentId.getUri()); if (widget == null || widget.getVisibility() == null){ return UserInterfaceElementVisibilityType.HIDDEN; } else { return widget.getVisibility(); } } }