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.enduser.panels.any;
20  
21  import java.util.ArrayList;
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.time.DateFormatUtils;
28  import org.apache.commons.lang3.time.FastDateFormat;
29  import org.apache.syncope.client.enduser.SyncopeEnduserSession;
30  import org.apache.syncope.client.enduser.layout.CustomizationOption;
31  import org.apache.syncope.client.enduser.markup.html.form.BinaryFieldPanel;
32  import org.apache.syncope.client.enduser.markup.html.form.MultiFieldPanel;
33  import org.apache.syncope.client.ui.commons.SchemaUtils;
34  import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
35  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
36  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateFieldPanel;
37  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
38  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
39  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
40  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
41  import org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel;
42  import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
43  import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
44  import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
45  import org.apache.syncope.common.lib.Attr;
46  import org.apache.syncope.common.lib.Attributable;
47  import org.apache.syncope.common.lib.EntityTOUtils;
48  import org.apache.syncope.common.lib.to.AnyTO;
49  import org.apache.syncope.common.lib.to.GroupableRelatableTO;
50  import org.apache.syncope.common.lib.to.MembershipTO;
51  import org.apache.syncope.common.lib.to.PlainSchemaTO;
52  import org.apache.syncope.common.lib.types.AttrSchemaType;
53  import org.apache.syncope.common.lib.types.SchemaType;
54  import org.apache.wicket.AttributeModifier;
55  import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
56  import org.apache.wicket.markup.html.WebMarkupContainer;
57  import org.apache.wicket.markup.html.basic.Label;
58  import org.apache.wicket.markup.html.form.IChoiceRenderer;
59  import org.apache.wicket.markup.html.list.ListItem;
60  import org.apache.wicket.markup.html.list.ListView;
61  import org.apache.wicket.model.IModel;
62  import org.apache.wicket.model.LoadableDetachableModel;
63  import org.apache.wicket.model.Model;
64  import org.apache.wicket.model.PropertyModel;
65  import org.apache.wicket.model.StringResourceModel;
66  import org.apache.wicket.model.util.ListModel;
67  
68  public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
69  
70      private static final long serialVersionUID = 552437609667518888L;
71  
72      protected final AnyTO previousObject;
73  
74      protected String fileKey = "";
75  
76      public PlainAttrs(
77              final String id,
78              final UserWrapper modelObject,
79              final List<String> anyTypeClasses,
80              final Map<String, CustomizationOption> whichPlainAttrs) throws IllegalArgumentException {
81  
82          super(id, modelObject, anyTypeClasses, whichPlainAttrs);
83  
84          fileKey = modelObject.getInnerObject().getUsername();
85  
86          previousObject = modelObject.getPreviousUserTO();
87  
88          add(new PlainSchemasOwn("plainSchemas", schemas, attrs).setOutputMarkupId(true));
89          add(new ListView<>("membershipsPlainSchemas", membershipTOs) {
90  
91              private static final long serialVersionUID = 6741044372185745296L;
92  
93              @Override
94              protected void populateItem(final ListItem<MembershipTO> item) {
95                  MembershipTO membershipTO = item.getModelObject();
96                  item.add(new Accordion("membershipPlainSchemas", List.of(new AbstractTab(
97                          new StringResourceModel(
98                                  "attributes.membership.accordion",
99                                  PlainAttrs.this,
100                                 Model.of(membershipTO))) {
101 
102                     private static final long serialVersionUID = 1037272333056449378L;
103 
104                     @Override
105                     public WebMarkupContainer getPanel(final String panelId) {
106                         return new PlainSchemasMemberships(
107                                 panelId,
108                                 membershipTO.getGroupName(),
109                                 membershipSchemas.get(membershipTO.getGroupKey()),
110                                 new LoadableDetachableModel<>() { // SYNCOPE-1439
111 
112                             private static final long serialVersionUID = 526768546610546553L;
113 
114                             @Override
115                             protected Attributable load() {
116                                 return membershipTO;
117                             }
118 
119                         });
120                     }
121                 }), Model.of(-1)).setOutputMarkupId(true));
122             }
123         });
124     }
125 
126     @Override
127     protected SchemaType getSchemaType() {
128         return SchemaType.PLAIN;
129     }
130 
131     @Override
132     protected List<Attr> getAttrsFromTO() {
133         return userTO.getPlainAttrs().stream().sorted(attrComparator).collect(Collectors.toList());
134     }
135 
136     @Override
137     protected List<Attr> getAttrsFromTO(final MembershipTO membershipTO) {
138         return membershipTO.getPlainAttrs().stream().sorted(attrComparator).collect(Collectors.toList());
139     }
140 
141     @Override
142     protected void setAttrs() {
143         List<Attr> plainAttrs = new ArrayList<>();
144 
145         Map<String, Attr> attrMap = EntityTOUtils.buildAttrMap(userTO.getPlainAttrs());
146 
147         plainAttrs.addAll(schemas.values().stream().map(schema -> {
148             Attr attrTO = new Attr();
149             attrTO.setSchema(schema.getKey());
150             if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
151                 attrTO.getValues().add("");
152             } else {
153                 attrTO = attrMap.get(schema.getKey());
154             }
155             return attrTO;
156         }).collect(Collectors.toList()));
157 
158         userTO.getPlainAttrs().clear();
159         userTO.getPlainAttrs().addAll(plainAttrs);
160     }
161 
162     @Override
163     protected void setAttrs(final MembershipTO membershipTO) {
164         Map<String, Attr> attrMap = GroupableRelatableTO.class.cast(userTO).getMembership(membershipTO.getGroupKey()).
165                 map(gr -> EntityTOUtils.buildAttrMap(gr.getPlainAttrs())).
166                 orElseGet(() -> new HashMap<>());
167 
168         List<Attr> plainAttrs = membershipSchemas.get(membershipTO.getGroupKey()).values().stream().map(schema -> {
169             Attr attr = new Attr();
170             attr.setSchema(schema.getKey());
171             if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
172                 attr.getValues().add(StringUtils.EMPTY);
173             } else {
174                 attr.getValues().addAll(attrMap.get(schema.getKey()).getValues());
175             }
176             return attr;
177         }).collect(Collectors.toList());
178 
179         membershipTO.getPlainAttrs().clear();
180         membershipTO.getPlainAttrs().addAll(plainAttrs);
181     }
182 
183     @SuppressWarnings({ "rawtypes", "unchecked" })
184     protected FieldPanel getFieldPanel(final PlainSchemaTO schemaTO) {
185         return getFieldPanel(schemaTO, null);
186     }
187 
188     @SuppressWarnings({ "rawtypes", "unchecked" })
189     protected FieldPanel getFieldPanel(final PlainSchemaTO plainSchema, final String groupName) {
190         boolean required = plainSchema.getMandatoryCondition().equalsIgnoreCase("true");
191         boolean readOnly = plainSchema.isReadonly() || renderAsReadonly(plainSchema.getKey(), groupName);
192         AttrSchemaType type = plainSchema.getType();
193         boolean jexlHelp = false;
194 
195         FieldPanel panel;
196         switch (type) {
197             case Boolean:
198                 panel = new AjaxCheckBoxPanel(
199                         "panel",
200                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()),
201                         new Model<>(),
202                         true);
203                 panel.setRequired(required);
204                 break;
205 
206             case Date:
207                 String datePattern = plainSchema.getConversionPattern() == null
208                         ? DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.getPattern()
209                         : plainSchema.getConversionPattern();
210 
211                 if (StringUtils.containsIgnoreCase(datePattern, "H")) {
212                     panel = new AjaxDateTimeFieldPanel(
213                             "panel",
214                             plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()),
215                             new Model<>(),
216                             FastDateFormat.getInstance(datePattern));
217                 } else {
218                     panel = new AjaxDateFieldPanel(
219                             "panel",
220                             plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()),
221                             new Model<>(),
222                             FastDateFormat.getInstance(datePattern));
223                 }
224 
225                 if (required) {
226                     panel.addRequiredLabel();
227                 }
228 
229                 break;
230 
231             case Enum:
232                 panel = new AjaxDropDownChoicePanel<>("panel",
233                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()), new Model<>(), true);
234                 ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(plainSchema));
235 
236                 if (StringUtils.isNotBlank(plainSchema.getEnumerationKeys())) {
237                     ((AjaxDropDownChoicePanel) panel).setChoiceRenderer(new IChoiceRenderer<String>() {
238 
239                         private static final long serialVersionUID = -3724971416312135885L;
240 
241                         private final Map<String, String> valueMap = SchemaUtils.getEnumeratedKeyValues(plainSchema);
242 
243                         @Override
244                         public String getDisplayValue(final String value) {
245                             return valueMap.get(value) == null ? value : valueMap.get(value);
246                         }
247 
248                         @Override
249                         public String getIdValue(final String value, final int i) {
250                             return value;
251                         }
252 
253                         @Override
254                         public String getObject(
255                                 final String id, final IModel<? extends List<? extends String>> choices) {
256                             return id;
257                         }
258                     });
259                 }
260 
261                 if (required) {
262                     panel.addRequiredLabel();
263                 }
264                 break;
265 
266             case Long:
267                 panel = new AjaxSpinnerFieldPanel.Builder<Long>().enableOnChange().build(
268                         "panel",
269                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()),
270                         Long.class,
271                         new Model<>());
272 
273                 if (required) {
274                     panel.addRequiredLabel();
275                 }
276                 break;
277 
278             case Double:
279                 panel = new AjaxSpinnerFieldPanel.Builder<Double>().enableOnChange().step(0.1).build(
280                         "panel",
281                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()),
282                         Double.class,
283                         new Model<>());
284 
285                 if (required) {
286                     panel.addRequiredLabel();
287                 }
288                 break;
289 
290             case Binary:
291                 panel = new BinaryFieldPanel(
292                         "panel",
293                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()),
294                         new Model<>(),
295                         plainSchema.getMimeType(),
296                         fileKey);
297                 if (required) {
298                     panel.addRequiredLabel();
299                 }
300                 break;
301 
302             case Encrypted:
303                 panel = new EncryptedFieldPanel("panel",
304                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()), new Model<>(), true);
305 
306                 if (required) {
307                     panel.addRequiredLabel();
308                 }
309                 break;
310 
311             default:
312                 panel = new AjaxTextFieldPanel("panel",
313                         plainSchema.getLabel(SyncopeEnduserSession.get().getLocale()), new Model<>(), true);
314 
315                 if (jexlHelp) {
316                     AjaxTextFieldPanel.class.cast(panel).enableJexlHelp();
317                 }
318 
319                 if (required) {
320                     panel.addRequiredLabel();
321                 }
322         }
323 
324         panel.setReadOnly(readOnly);
325         panel.setMarkupId(StringUtils.isBlank(groupName)
326                 ? plainSchema.getKey() : groupName + '.' + plainSchema.getKey());
327 
328         Label label = (Label) panel.get(AbstractFieldPanel.LABEL);
329         label.add(new AttributeModifier("for", FORM_SUFFIX
330                 + (StringUtils.isBlank(groupName) ? plainSchema.getKey() : groupName + '.' + plainSchema.getKey())));
331 
332         return panel;
333     }
334 
335     protected class PlainSchemasMemberships extends Schemas {
336 
337         private static final long serialVersionUID = 456754923340249215L;
338 
339         public PlainSchemasMemberships(
340                 final String id,
341                 final String groupName,
342                 final Map<String, PlainSchemaTO> schemas,
343                 final IModel<Attributable> attributableTO) {
344 
345             super(id);
346 
347             add(new ListView<>("schemas",
348                     new ListModel<>(new ArrayList<>(
349                             attributableTO.getObject().getPlainAttrs().stream().sorted(attrComparator).
350                                     collect(Collectors.toList())))) {
351 
352                 private static final long serialVersionUID = 5306618783986001008L;
353 
354                 @Override
355                 @SuppressWarnings({ "unchecked", "rawtypes" })
356                 protected void populateItem(final ListItem<Attr> item) {
357                     Attr attrTO = item.getModelObject();
358                     PlainSchemaTO schema = schemas.get(attrTO.getSchema());
359 
360                     // set default values, if any
361                     if (attrTO.getValues().stream().noneMatch(StringUtils::isNotBlank)) {
362                         attrTO.getValues().clear();
363                         attrTO.getValues().addAll(getDefaultValues(attrTO.getSchema(), groupName));
364                     }
365 
366                     AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attrTO.getSchema()));
367                     if (schemas.get(attrTO.getSchema()).isMultivalue()) {
368                         panel = new MultiFieldPanel.Builder<>(new PropertyModel<>(attrTO, "values")).
369                                 build("panel", attrTO.getSchema(), FieldPanel.class.cast(panel));
370                         // SYNCOPE-1215 the entire multifield panel must be readonly, not only its field
371                         ((MultiFieldPanel) panel).setReadOnly(schema == null ? false : schema.isReadonly());
372                     } else {
373                         FieldPanel.class.cast(panel).setNewModel(attrTO.getValues()).
374                                 setReadOnly(schema == null ? false : schema.isReadonly());
375                     }
376 
377                     item.add(panel);
378                 }
379             });
380         }
381     }
382 
383     protected class PlainSchemasOwn extends Schemas {
384 
385         private static final long serialVersionUID = -4730563859116024676L;
386 
387         public PlainSchemasOwn(
388                 final String id,
389                 final Map<String, PlainSchemaTO> schemas,
390                 final IModel<List<Attr>> attrTOs) {
391 
392             super(id);
393 
394             add(new ListView<>("schemas", attrTOs) {
395 
396                 private static final long serialVersionUID = 9101744072914090143L;
397 
398                 @Override
399                 @SuppressWarnings({ "unchecked", "rawtypes" })
400                 protected void populateItem(final ListItem<Attr> item) {
401                     Attr attrTO = item.getModelObject();
402                     PlainSchemaTO schema = schemas.get(attrTO.getSchema());
403 
404                     // set default values, if any
405                     if (attrTO.getValues().stream().noneMatch(StringUtils::isNotBlank)) {
406                         attrTO.getValues().clear();
407                         attrTO.getValues().addAll(getDefaultValues(attrTO.getSchema()));
408                     }
409 
410                     AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attrTO.getSchema()));
411                     if (schemas.get(attrTO.getSchema()).isMultivalue()) {
412                         panel = new MultiFieldPanel.Builder<>(
413                                 new PropertyModel<>(attrTO, "values")).build(
414                                 "panel",
415                                 attrTO.getSchema(),
416                                 FieldPanel.class.cast(panel));
417                         // SYNCOPE-1215 the entire multifield panel must be readonly, not only its field
418                         ((MultiFieldPanel) panel).setReadOnly(schema == null ? false : schema.isReadonly());
419                     } else {
420                         FieldPanel.class.cast(panel).setNewModel(attrTO.getValues()).
421                                 setReadOnly(schema == null ? false : schema.isReadonly());
422                     }
423                     item.add(panel);
424                 }
425             });
426         }
427     }
428 }