blob: 17499d3de17ba4827c3207128beaf5d6099f327d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.syncope.client.console.panels;
import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverBehavior;
import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverConfig;
import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.console.pages.BasePage;
import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
import org.apache.syncope.client.console.panels.search.AbstractSearchPanel;
import org.apache.syncope.client.console.panels.search.ConnObjectSearchPanel;
import org.apache.syncope.client.console.panels.search.SearchClause;
import org.apache.syncope.client.console.panels.search.SearchClausePanel;
import org.apache.syncope.client.console.panels.search.SearchUtils;
import org.apache.syncope.client.console.rest.AnyTypeRestClient;
import org.apache.syncope.client.console.rest.ReconciliationRestClient;
import org.apache.syncope.client.console.rest.ResourceRestClient;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CollectionPanel;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.status.StatusUtils;
import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.types.IdMEntitlement;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.ReconStatus;
import org.apache.syncope.common.lib.to.ResourceTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.common.lib.types.MatchType;
import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery;
import org.apache.syncope.common.rest.api.beans.ReconQuery;
import org.apache.wicket.Component;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.model.util.ListModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class ConnObjectListViewPanel extends Panel {
private static final long serialVersionUID = 4986172040062752781L;
private static final Logger LOG = LoggerFactory.getLogger(ConnObjectListViewPanel.class);
private static final int SIZE = 10;
private static final String STATUS = "Status";
private String nextPageCookie;
private AbstractSearchPanel searchPanel;
private WebMarkupContainer arrows;
private String anyType;
private ResourceTO resource;
protected ConnObjectListViewPanel(
final String id,
final ResourceTO resource,
final String anyType,
final PageReference pageRef) {
super(id);
this.anyType = anyType;
this.resource = resource;
final Model<Integer> model = Model.of(-1);
final StringResourceModel res = new StringResourceModel("search.result", this, new Model<>(anyType));
final Accordion accordion = new Accordion("accordionPanel", List.of(new AbstractTab(res) {
private static final long serialVersionUID = 1037272333056449377L;
@Override
public WebMarkupContainer getPanel(final String panelId) {
searchPanel = getSearchPanel(panelId, anyType);
return searchPanel;
}
}), model) {
private static final long serialVersionUID = 6581261306163L;
@Override
protected Component newTitle(final String markupId, final ITab tab, final Accordion.State state) {
return new AjaxLink<Integer>(markupId) {
private static final long serialVersionUID = 6584438659172L;
@Override
protected void onComponentTag(final ComponentTag tag) {
super.onComponentTag(tag);
tag.put("style", "color: #337ab7");
}
@Override
public void onClick(final AjaxRequestTarget target) {
model.setObject(model.getObject() == 0 ? -1 : 0);
}
}.setBody(res);
}
};
accordion.setOutputMarkupId(true);
add(accordion.setEnabled(true).setVisible(true));
List<ConnObjectTO> listOfItems = reloadItems(resource.getKey(), anyType, null, null);
ListViewPanel.Builder<ConnObjectTO> builder = new ListViewPanel.Builder<>(
ConnObjectTO.class, pageRef) {
private static final long serialVersionUID = -8251750413385566738L;
@Override
protected Component getValueComponent(final String key, final ConnObjectTO bean) {
if (StringUtils.equals(key, STATUS)) {
ReconStatus status;
try {
status = ReconciliationRestClient.status(
new ReconQuery.Builder(anyType, resource.getKey()).fiql(bean.getFiql()).build());
} catch (Exception e) {
LOG.error("While requesting for reconciliation status of {} {} with FIQL '{}'",
anyType, resource.getKey(), bean.getFiql(), e);
status = new ReconStatus();
}
return status.getOnSyncope() == null
? StatusUtils.getLabel("field", "notfound icon", "Not found", Constants.NOT_FOUND_ICON)
: new Label("field", Model.of()).add(new PopoverBehavior(
Model.of(),
Model.of(status.getAnyKey()),
new PopoverConfig().
withTitle(status.getMatchType() == MatchType.LINKED_ACCOUNT
? MatchType.LINKED_ACCOUNT.name() + ", " + AnyTypeKind.USER
: status.getAnyTypeKind().name()).
withPlacement(TooltipConfig.Placement.left)) {
private static final long serialVersionUID = -7867802555691605021L;
@Override
protected String createRelAttribute() {
return "field";
}
@Override
public void onComponentTag(final Component component, final ComponentTag tag) {
super.onComponentTag(component, tag);
tag.put("class", Constants.ACTIVE_ICON);
}
});
} else {
Optional<Attr> attr =
bean.getAttrs().stream().filter(object -> object.getSchema().equals(key)).findAny();
return attr.isEmpty() || attr.get().getValues().isEmpty()
? new Label("field", StringUtils.EMPTY)
: new CollectionPanel("field", attr.get().getValues());
}
}
};
builder.setReuseItem(false);
builder.addAction(new ActionLink<>() {
private static final long serialVersionUID = 7511002881490248598L;
@Override
public void onClick(final AjaxRequestTarget target, final ConnObjectTO modelObject) {
viewConnObject(modelObject, target);
}
}, ActionLink.ActionType.VIEW, IdMEntitlement.RESOURCE_GET_CONNOBJECT).
setItems(listOfItems).
includes(ConnIdSpecialName.UID,
ConnIdSpecialName.NAME,
ConnIdSpecialName.ENABLE).
withChecks(ListViewPanel.CheckAvailability.NONE).
setReuseItem(false);
if (!StringUtils.equals(anyType, SyncopeConstants.REALM_ANYTYPE)) {
builder.addAction(new ActionLink<>() {
private static final long serialVersionUID = 6377238742125L;
@Override
public void onClick(final AjaxRequestTarget target, final ConnObjectTO modelObject) {
try {
ReconStatus status = ReconciliationRestClient.status(
new ReconQuery.Builder(anyType, resource.getKey()).fiql(modelObject.getFiql()).build());
pullConnObject(
modelObject.getFiql(),
target,
resource.getKey(),
anyType,
status.getRealm(),
StringUtils.isNotBlank(status.getAnyKey()),
pageRef);
} catch (Exception e) {
LOG.error("While puling single object {} {} with FIQL '{}'",
anyType, resource.getKey(), modelObject.getFiql(), e);
SyncopeConsoleSession.get().onException(e);
((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
}
}
}, ActionLink.ActionType.RECONCILIATION_PULL, IdRepoEntitlement.TASK_EXECUTE);
builder.includes(STATUS);
}
add(builder.build("objs"));
arrows = new WebMarkupContainer("arrows");
add(arrows.setOutputMarkupId(true));
arrows.add(new AjaxLink<Serializable>("next") {
private static final long serialVersionUID = -7978723352517770644L;
@Override
public void onClick(final AjaxRequestTarget target) {
List<ConnObjectTO> listOfItems = reloadItems(resource.getKey(), anyType, nextPageCookie, getFiql());
target.add(arrows);
send(ConnObjectListViewPanel.this, Broadcast.DEPTH, new ListViewReload<>(listOfItems, target));
}
@Override
public boolean isVisible() {
return nextPageCookie != null;
}
});
}
@Override
public void onEvent(final IEvent<?> event) {
if (event.getPayload() instanceof SearchClausePanel.SearchEvent) {
this.nextPageCookie = null;
AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
List<ConnObjectTO> listOfItems = reloadItems(resource.getKey(), anyType, null, getFiql());
target.add(arrows);
send(ConnObjectListViewPanel.this, Broadcast.DEPTH, new ListViewReload<>(listOfItems, target));
} else {
super.onEvent(event);
}
}
protected abstract void viewConnObject(ConnObjectTO connObjectTO, AjaxRequestTarget target);
protected abstract void pullConnObject(
String fiql,
AjaxRequestTarget target,
String resource,
String anyType,
String realm,
boolean isOnSyncope,
PageReference pageRef);
private List<ConnObjectTO> reloadItems(
final String resource,
final String anyType,
final String cookie,
final String fiql) {
Pair<String, List<ConnObjectTO>> items = new ResourceRestClient().searchConnObjects(resource,
anyType,
new ConnObjectTOQuery.Builder().
size(SIZE).
pagedResultsCookie(cookie).
fiql(fiql),
new SortParam<>(ConnIdSpecialName.UID, true));
nextPageCookie = items.getLeft();
return items.getRight();
}
private AbstractSearchPanel getSearchPanel(final String id, final String anyType) {
final List<SearchClause> clauses = new ArrayList<>();
final SearchClause clause = new SearchClause();
clauses.add(clause);
clause.setComparator(SearchClause.Comparator.EQUALS);
clause.setType(SearchClause.Type.ATTRIBUTE);
clause.setProperty("");
AnyTypeKind anyTypeKind =
StringUtils.equals(anyType, SyncopeConstants.REALM_ANYTYPE) || StringUtils.isEmpty(anyType)
? AnyTypeKind.ANY_OBJECT
: AnyTypeRestClient.read(anyType).getKind();
return new ConnObjectSearchPanel.Builder(resource, anyTypeKind, anyType,
new ListModel<>(clauses)).required(true).enableSearch().build(id);
}
private String getFiql() {
return SearchUtils.buildFIQL(
searchPanel.getModel().getObject(),
SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder(),
searchPanel.getAvailableSchemaTypes(),
SearchUtils.NO_CUSTOM_CONDITION);
}
}