View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.client.console.panels.search;
20  
21  import java.io.Serializable;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.stream.Collectors;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.commons.lang3.tuple.Pair;
28  import org.apache.syncope.client.console.SyncopeConsoleSession;
29  import org.apache.syncope.client.console.SyncopeWebApplication;
30  import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
31  import org.apache.syncope.client.console.rest.FIQLQueryRestClient;
32  import org.apache.syncope.client.console.rest.GroupRestClient;
33  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
34  import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
35  import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
36  import org.apache.syncope.common.lib.SyncopeConstants;
37  import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
38  import org.apache.syncope.common.lib.search.SearchableFields;
39  import org.apache.syncope.common.lib.to.AnyTypeClassTO;
40  import org.apache.syncope.common.lib.to.PlainSchemaTO;
41  import org.apache.syncope.common.lib.types.AnyTypeKind;
42  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
43  import org.apache.wicket.PageReference;
44  import org.apache.wicket.ajax.AjaxRequestTarget;
45  import org.apache.wicket.event.IEventSink;
46  import org.apache.wicket.markup.html.WebMarkupContainer;
47  import org.apache.wicket.markup.html.panel.Panel;
48  import org.apache.wicket.model.IModel;
49  import org.apache.wicket.model.LoadableDetachableModel;
50  import org.apache.wicket.model.Model;
51  import org.apache.wicket.spring.injection.annot.SpringBean;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  public abstract class AbstractSearchPanel extends Panel {
56  
57      private static final long serialVersionUID = 5922413053568696414L;
58  
59      protected static final Logger LOG = LoggerFactory.getLogger(AbstractSearchPanel.class);
60  
61      @SpringBean
62      protected AnyTypeClassRestClient anyTypeClassRestClient;
63  
64      @SpringBean
65      protected FIQLQueryRestClient fiqlQueryRestClient;
66  
67      @SpringBean
68      protected GroupRestClient groupRestClient;
69  
70      protected IModel<Map<String, PlainSchemaTO>> dnames;
71  
72      protected IModel<Map<String, PlainSchemaTO>> anames;
73  
74      protected IModel<List<String>> auxClassNames;
75  
76      protected IModel<List<String>> resourceNames;
77  
78      protected IModel<List<SearchClause.Type>> types;
79  
80      protected IModel<List<String>> groupNames;
81  
82      protected IModel<List<String>> roleNames;
83  
84      protected IModel<List<String>> privilegeNames;
85  
86      protected IModel<List<SearchClause>> model;
87  
88      protected WebMarkupContainer searchFormContainer;
89  
90      protected final AnyTypeKind typeKind;
91  
92      protected final String type;
93  
94      public abstract static class Builder<T extends AbstractSearchPanel> implements Serializable {
95  
96          private static final long serialVersionUID = 6308997285778809578L;
97  
98          protected final IModel<List<SearchClause>> model;
99  
100         protected PageReference pageRef;
101 
102         protected boolean required = true;
103 
104         protected boolean enableSearch = false;
105 
106         protected SearchClausePanel.Customizer customizer = new SearchClausePanel.Customizer() {
107 
108             private static final long serialVersionUID = 4449199405807453441L;
109 
110         };
111 
112         protected IEventSink resultContainer;
113 
114         public Builder(final IModel<List<SearchClause>> model, final PageReference pageRef) {
115             this.model = model;
116             this.pageRef = pageRef;
117         }
118 
119         public Builder<T> enableSearch(final IEventSink resultContainer) {
120             this.resultContainer = resultContainer;
121             return enableSearch();
122         }
123 
124         public Builder<T> enableSearch() {
125             this.enableSearch = true;
126             return this;
127         }
128 
129         public Builder<T> customizer(final SearchClausePanel.Customizer customizer) {
130             this.customizer = customizer;
131             return this;
132         }
133 
134         public Builder<T> required(final boolean required) {
135             this.required = required;
136             return this;
137         }
138 
139         public abstract T build(String id);
140     }
141 
142     protected AbstractSearchPanel(final String id, final AnyTypeKind kind, final Builder<?> builder) {
143         this(id, kind, kind.name(), builder);
144     }
145 
146     protected AbstractSearchPanel(
147             final String id, final AnyTypeKind kind, final String type, final Builder<?> builder) {
148 
149         super(id);
150 
151         populate();
152 
153         this.model = builder.model;
154         this.typeKind = kind;
155         this.type = type;
156 
157         setOutputMarkupId(true);
158 
159         searchFormContainer = new WebMarkupContainer("searchFormContainer");
160         searchFormContainer.setOutputMarkupId(true);
161         add(searchFormContainer);
162 
163         Pair<IModel<List<String>>, IModel<Integer>> groupInfo =
164                 typeKind != AnyTypeKind.GROUP && SyncopeConsoleSession.get().owns(IdRepoEntitlement.GROUP_SEARCH)
165                 ? Pair.of(groupNames, new LoadableDetachableModel<>() {
166 
167                     private static final long serialVersionUID = 7362833782319137329L;
168 
169                     @Override
170                     protected Integer load() {
171                         return groupRestClient.count(SyncopeConstants.ROOT_REALM, null, null);
172                     }
173                 })
174                 : Pair.of(groupNames, Model.of(0));
175         SearchClausePanel searchClausePanel = new SearchClausePanel("panel", "panel",
176                 Model.of(new SearchClause()),
177                 builder.required,
178                 types,
179                 builder.customizer,
180                 anames, dnames, groupInfo, roleNames, privilegeNames, auxClassNames, resourceNames);
181         if (builder.enableSearch) {
182             searchClausePanel.enableSearch(builder.resultContainer);
183         }
184 
185         searchFormContainer.add(new MultiFieldPanel.Builder<>(model) {
186 
187             private static final long serialVersionUID = 1343431509987473047L;
188 
189             @Override
190             protected SearchClause newModelObject() {
191                 return new SearchClause();
192             }
193         }.build("search", "search", searchClausePanel).hideLabel().setOutputMarkupId(true));
194 
195         FIQLQueries fiqlQueries = new FIQLQueries(
196                 "fiqlQueries", fiqlQueryRestClient, this, getFIQLQueryTarget(), builder.pageRef);
197         add(fiqlQueries);
198 
199         SaveFIQLQuery saveFIQLQuery = new SaveFIQLQuery("saveFIQLQuery", getFIQLQueryTarget(), builder.pageRef);
200         add(saveFIQLQuery);
201 
202         ActionsPanel<Serializable> fiqlQueryActionsPanel = new ActionsPanel<>("fiqlQueryActionsPanel", null);
203         fiqlQueryActionsPanel.add(new ActionLink<>() {
204 
205             private static final long serialVersionUID = 2041211756396714619L;
206 
207             @Override
208             public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
209                 saveFIQLQuery.setFiql(
210                         SearchUtils.buildFIQL(AbstractSearchPanel.this.getModel().getObject(),
211                                 getSearchConditionBuilder()).
212                                 replaceAll(SearchUtils.getTypeConditionPattern(type).pattern(), ""));
213                 saveFIQLQuery.toggle(target, true);
214             }
215         }, ActionLink.ActionType.EXPORT, StringUtils.EMPTY).hideLabel();
216         fiqlQueryActionsPanel.add(new ActionLink<>() {
217 
218             private static final long serialVersionUID = -7978723352517770644L;
219 
220             @Override
221             public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
222                 fiqlQueries.toggle(target, true);
223             }
224         }, ActionLink.ActionType.SELECT, StringUtils.EMPTY).hideLabel();
225         fiqlQueryActionsPanel.setVisible(
226                 builder.enableSearch
227                 && !model.getObject().isEmpty()
228                 && !SyncopeConsoleSession.get().getSelfTO().getUsername().
229                         equals(SyncopeWebApplication.get().getAdminUser()));
230         add(fiqlQueryActionsPanel.setOutputMarkupPlaceholderTag(true));
231     }
232 
233     protected abstract AbstractFiqlSearchConditionBuilder<?, ?, ?> getSearchConditionBuilder();
234 
235     protected abstract String getFIQLQueryTarget();
236 
237     protected void updateFIQL(final AjaxRequestTarget target, final String fiql) {
238         model.setObject(SearchUtils.getSearchClauses(
239                 fiql.replaceAll(SearchUtils.getTypeConditionPattern(type).pattern(), "")));
240         target.add(searchFormContainer);
241     }
242 
243     protected void populate() {
244         dnames = new LoadableDetachableModel<>() {
245 
246             private static final long serialVersionUID = 5275935387613157437L;
247 
248             @Override
249             protected Map<String, PlainSchemaTO> load() {
250                 Map<String, PlainSchemaTO> dSchemaNames = new HashMap<>();
251                 SearchableFields.get(typeKind.getTOClass()).forEach((key, type) -> {
252                     PlainSchemaTO plain = new PlainSchemaTO();
253                     plain.setType(type);
254                     dSchemaNames.put(key, plain);
255                 });
256                 return dSchemaNames;
257             }
258         };
259 
260         auxClassNames = new LoadableDetachableModel<>() {
261 
262             private static final long serialVersionUID = 5275935387613157437L;
263 
264             @Override
265             protected List<String> load() {
266                 return anyTypeClassRestClient.list().stream().
267                         filter(c -> c.getInUseByTypes().isEmpty()).
268                         map(AnyTypeClassTO::getKey).
269                         collect(Collectors.toList());
270             }
271         };
272 
273         resourceNames = new LoadableDetachableModel<>() {
274 
275             private static final long serialVersionUID = 5275935387613157437L;
276 
277             @Override
278             protected List<String> load() {
279                 return SyncopeWebApplication.get().getResourceProvider().get();
280             }
281         };
282     }
283 
284     public IModel<List<SearchClause>> getModel() {
285         return this.model;
286     }
287 
288     public String getBackObjectType() {
289         return this.type;
290     }
291 
292     public Map<String, PlainSchemaTO> getAvailableSchemaTypes() {
293         return anames.getObject();
294     }
295 }