1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.client.console.panels;
20
21 import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverBehavior;
22 import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverConfig;
23 import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
24 import java.io.Serializable;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Optional;
28 import org.apache.commons.lang3.StringUtils;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.apache.syncope.client.console.SyncopeConsoleSession;
31 import org.apache.syncope.client.console.pages.BasePage;
32 import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
33 import org.apache.syncope.client.console.panels.search.AbstractSearchPanel;
34 import org.apache.syncope.client.console.panels.search.ConnObjectSearchPanel;
35 import org.apache.syncope.client.console.panels.search.SearchClause;
36 import org.apache.syncope.client.console.panels.search.SearchClausePanel;
37 import org.apache.syncope.client.console.panels.search.SearchUtils;
38 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
39 import org.apache.syncope.client.console.rest.ReconciliationRestClient;
40 import org.apache.syncope.client.console.rest.ResourceRestClient;
41 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CollectionPanel;
42 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
43 import org.apache.syncope.client.lib.SyncopeClient;
44 import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
45 import org.apache.syncope.client.ui.commons.Constants;
46 import org.apache.syncope.client.ui.commons.status.StatusUtils;
47 import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
48 import org.apache.syncope.common.lib.Attr;
49 import org.apache.syncope.common.lib.SyncopeConstants;
50 import org.apache.syncope.common.lib.to.ConnObject;
51 import org.apache.syncope.common.lib.to.ReconStatus;
52 import org.apache.syncope.common.lib.to.ResourceTO;
53 import org.apache.syncope.common.lib.types.AnyTypeKind;
54 import org.apache.syncope.common.lib.types.IdMEntitlement;
55 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
56 import org.apache.syncope.common.lib.types.MatchType;
57 import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery;
58 import org.apache.syncope.common.rest.api.beans.ReconQuery;
59 import org.apache.wicket.Component;
60 import org.apache.wicket.PageReference;
61 import org.apache.wicket.ajax.AjaxRequestTarget;
62 import org.apache.wicket.ajax.markup.html.AjaxLink;
63 import org.apache.wicket.event.Broadcast;
64 import org.apache.wicket.event.IEvent;
65 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
66 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
67 import org.apache.wicket.extensions.markup.html.tabs.ITab;
68 import org.apache.wicket.markup.ComponentTag;
69 import org.apache.wicket.markup.html.WebMarkupContainer;
70 import org.apache.wicket.markup.html.basic.Label;
71 import org.apache.wicket.markup.html.panel.Panel;
72 import org.apache.wicket.model.Model;
73 import org.apache.wicket.model.StringResourceModel;
74 import org.apache.wicket.model.util.ListModel;
75 import org.apache.wicket.spring.injection.annot.SpringBean;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 public abstract class ConnObjectListViewPanel extends Panel {
80
81 private static final long serialVersionUID = 4986172040062752781L;
82
83 protected static final Logger LOG = LoggerFactory.getLogger(ConnObjectListViewPanel.class);
84
85 protected static final int SIZE = 10;
86
87 protected static final String STATUS = "Status";
88
89 @SpringBean
90 protected ReconciliationRestClient reconciliationRestClient;
91
92 @SpringBean
93 protected ResourceRestClient resourceRestClient;
94
95 @SpringBean
96 protected AnyTypeRestClient anyTypeRestClient;
97
98 protected String nextPageCookie;
99
100 protected AbstractSearchPanel searchPanel;
101
102 protected WebMarkupContainer arrows;
103
104 protected String anyType;
105
106 protected ResourceTO resource;
107
108 protected final PageReference pageRef;
109
110 protected ConnObjectListViewPanel(
111 final String id,
112 final ResourceTO resource,
113 final String anyType,
114 final PageReference pageRef) {
115
116 super(id);
117
118 this.anyType = anyType;
119 this.resource = resource;
120 this.pageRef = pageRef;
121
122 final Model<Integer> model = Model.of(-1);
123 final StringResourceModel res = new StringResourceModel("search.result", this, new Model<>(anyType));
124
125 final Accordion accordion = new Accordion("accordionPanel", List.of(new AbstractTab(res) {
126
127 private static final long serialVersionUID = 1037272333056449377L;
128
129 @Override
130 public WebMarkupContainer getPanel(final String panelId) {
131 searchPanel = getSearchPanel(panelId, anyType);
132 return searchPanel;
133 }
134
135 }), model) {
136
137 private static final long serialVersionUID = 6581261306163L;
138
139 @Override
140 protected Component newTitle(final String markupId, final ITab tab, final Accordion.State state) {
141 return new AjaxLink<Integer>(markupId) {
142
143 private static final long serialVersionUID = 6584438659172L;
144
145 @Override
146 protected void onComponentTag(final ComponentTag tag) {
147 super.onComponentTag(tag);
148 tag.put("style", "color: #337ab7");
149 }
150
151 @Override
152 public void onClick(final AjaxRequestTarget target) {
153 model.setObject(model.getObject() == 0 ? -1 : 0);
154 }
155 }.setBody(res);
156 }
157 };
158 accordion.setOutputMarkupId(true);
159 add(accordion.setEnabled(true).setVisible(true));
160
161 List<ConnObject> listOfItems = reloadItems(resource.getKey(), anyType, null, null);
162
163 ListViewPanel.Builder<ConnObject> builder = new ListViewPanel.Builder<>(
164 ConnObject.class, pageRef) {
165
166 private static final long serialVersionUID = -8251750413385566738L;
167
168 @Override
169 protected Component getValueComponent(final String key, final ConnObject bean) {
170 if (StringUtils.equals(key, STATUS)) {
171 ReconStatus status;
172 try {
173 status = reconciliationRestClient.status(
174 new ReconQuery.Builder(anyType, resource.getKey()).fiql(bean.getFiql()).build());
175 } catch (Exception e) {
176 LOG.error("While requesting for reconciliation status of {} {} with FIQL '{}'",
177 anyType, resource.getKey(), bean.getFiql(), e);
178
179 status = new ReconStatus();
180 }
181
182 return status.getOnSyncope() == null
183 ? StatusUtils.getLabel("field", "notfound icon", "Not found", Constants.NOT_FOUND_ICON)
184 : new Label("field", Model.of()).add(new PopoverBehavior(
185 Model.of(),
186 Model.of(status.getAnyKey()),
187 new PopoverConfig().
188 withTitle(status.getMatchType() == MatchType.LINKED_ACCOUNT
189 ? MatchType.LINKED_ACCOUNT.name() + ", " + AnyTypeKind.USER
190 : status.getAnyTypeKind().name()).
191 withPlacement(TooltipConfig.Placement.left)) {
192
193 private static final long serialVersionUID = -7867802555691605021L;
194
195 @Override
196 protected String createRelAttribute() {
197 return "field";
198 }
199
200 @Override
201 public void onComponentTag(final Component component, final ComponentTag tag) {
202 super.onComponentTag(component, tag);
203 tag.put("class", Constants.ACTIVE_ICON);
204 }
205 });
206 } else {
207 Optional<Attr> attr = bean.getAttrs().stream().
208 filter(object -> object.getSchema().equals(key)).findAny();
209 return attr.filter(a -> !a.getValues().isEmpty()).
210 map(a -> (Component) new CollectionPanel("field", a.getValues())).
211 orElseGet(() -> new Label("field", StringUtils.EMPTY));
212 }
213 }
214
215 };
216
217 builder.setReuseItem(false);
218 builder.addAction(new ActionLink<>() {
219
220 private static final long serialVersionUID = 7511002881490248598L;
221
222 @Override
223 public void onClick(final AjaxRequestTarget target, final ConnObject modelObject) {
224 viewConnObject(modelObject, target);
225 }
226 }, ActionLink.ActionType.VIEW, IdMEntitlement.RESOURCE_GET_CONNOBJECT).
227 setItems(listOfItems).
228 includes(ConnIdSpecialName.UID,
229 ConnIdSpecialName.NAME,
230 ConnIdSpecialName.ENABLE).
231 withChecks(ListViewPanel.CheckAvailability.NONE).
232 setReuseItem(false);
233
234 if (!StringUtils.equals(anyType, SyncopeConstants.REALM_ANYTYPE)) {
235 builder.addAction(new ActionLink<>() {
236
237 private static final long serialVersionUID = 6377238742125L;
238
239 @Override
240 public void onClick(final AjaxRequestTarget target, final ConnObject modelObject) {
241 try {
242 ReconStatus status = reconciliationRestClient.status(
243 new ReconQuery.Builder(anyType, resource.getKey()).fiql(modelObject.getFiql()).build());
244
245 pullConnObject(
246 modelObject.getFiql(),
247 target,
248 resource.getKey(),
249 anyType,
250 status.getRealm(),
251 StringUtils.isNotBlank(status.getAnyKey()),
252 pageRef);
253 } catch (Exception e) {
254 LOG.error("While puling single object {} {} with FIQL '{}'",
255 anyType, resource.getKey(), modelObject.getFiql(), e);
256
257 SyncopeConsoleSession.get().onException(e);
258 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
259 }
260 }
261 }, ActionLink.ActionType.RECONCILIATION_PULL, IdRepoEntitlement.TASK_EXECUTE);
262
263 builder.includes(STATUS);
264 }
265
266 add(builder.build("objs"));
267
268 arrows = new WebMarkupContainer("arrows");
269 add(arrows.setOutputMarkupId(true));
270
271 arrows.add(new AjaxLink<Serializable>("next") {
272
273 private static final long serialVersionUID = -7978723352517770644L;
274
275 @Override
276 public void onClick(final AjaxRequestTarget target) {
277 List<ConnObject> listOfItems = reloadItems(resource.getKey(), anyType, nextPageCookie, getFiql());
278 target.add(arrows);
279 send(ConnObjectListViewPanel.this, Broadcast.DEPTH, new ListViewReload<>(listOfItems, target));
280 }
281
282 @Override
283 public boolean isVisible() {
284 return nextPageCookie != null;
285 }
286 });
287 }
288
289 @Override
290 public void onEvent(final IEvent<?> event) {
291 if (event.getPayload() instanceof SearchClausePanel.SearchEvent) {
292 this.nextPageCookie = null;
293 AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
294 List<ConnObject> listOfItems = reloadItems(resource.getKey(), anyType, null, getFiql());
295 target.add(arrows);
296 send(ConnObjectListViewPanel.this, Broadcast.DEPTH, new ListViewReload<>(listOfItems, target));
297 } else {
298 super.onEvent(event);
299 }
300 }
301
302 protected abstract void viewConnObject(ConnObject connObjectTO, AjaxRequestTarget target);
303
304 protected abstract void pullConnObject(
305 String fiql,
306 AjaxRequestTarget target,
307 String resource,
308 String anyType,
309 String realm,
310 boolean isOnSyncope,
311 PageReference pageRef);
312
313 protected List<ConnObject> reloadItems(
314 final String resource,
315 final String anyType,
316 final String cookie,
317 final String fiql) {
318
319 Pair<String, List<ConnObject>> items = resourceRestClient.searchConnObjects(
320 resource,
321 anyType,
322 new ConnObjectTOQuery.Builder().
323 size(SIZE).
324 pagedResultsCookie(cookie).
325 fiql(fiql),
326 new SortParam<>(ConnIdSpecialName.UID, true));
327
328 nextPageCookie = items.getLeft();
329 return items.getRight();
330 }
331
332 protected AbstractSearchPanel getSearchPanel(final String id, final String anyType) {
333 final List<SearchClause> clauses = new ArrayList<>();
334 final SearchClause clause = new SearchClause();
335 clauses.add(clause);
336
337 clause.setComparator(SearchClause.Comparator.EQUALS);
338 clause.setType(SearchClause.Type.ATTRIBUTE);
339 clause.setProperty("");
340
341 AnyTypeKind anyTypeKind =
342 StringUtils.equals(anyType, SyncopeConstants.REALM_ANYTYPE) || StringUtils.isEmpty(anyType)
343 ? AnyTypeKind.ANY_OBJECT
344 : anyTypeRestClient.read(anyType).getKind();
345
346 return new ConnObjectSearchPanel.Builder(resource, anyTypeKind, anyType,
347 new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
348 }
349
350 protected String getFiql() {
351 return SearchUtils.buildFIQL(
352 searchPanel.getModel().getObject(),
353 SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder(),
354 searchPanel.getAvailableSchemaTypes(),
355 SearchUtils.NO_CUSTOM_CONDITION);
356 }
357 }