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.wizards.any;
20  
21  import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.bootstraptoggle.BootstrapToggle;
22  import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.bootstraptoggle.BootstrapToggleConfig;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.stream.Collectors;
30  import org.apache.syncope.client.console.commons.LinkedAccountPlainAttrProperty;
31  import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
32  import org.apache.syncope.client.ui.commons.Constants;
33  import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
34  import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
35  import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
36  import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
37  import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
38  import org.apache.syncope.client.ui.commons.wizards.any.EntityWrapper;
39  import org.apache.syncope.common.lib.Attr;
40  import org.apache.syncope.common.lib.EntityTOUtils;
41  import org.apache.syncope.common.lib.to.AnyTO;
42  import org.apache.syncope.common.lib.to.LinkedAccountTO;
43  import org.apache.syncope.common.lib.to.PlainSchemaTO;
44  import org.apache.syncope.common.lib.to.UserTO;
45  import org.apache.syncope.common.lib.types.SchemaType;
46  import org.apache.wicket.ajax.AjaxRequestTarget;
47  import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
48  import org.apache.wicket.markup.ComponentTag;
49  import org.apache.wicket.markup.html.WebMarkupContainer;
50  import org.apache.wicket.markup.html.form.CheckBox;
51  import org.apache.wicket.markup.html.form.FormComponent;
52  import org.apache.wicket.markup.html.list.ListItem;
53  import org.apache.wicket.markup.html.list.ListView;
54  import org.apache.wicket.model.IModel;
55  import org.apache.wicket.model.Model;
56  import org.apache.wicket.model.PropertyModel;
57  import org.apache.wicket.model.ResourceModel;
58  
59  public class LinkedAccountPlainAttrsPanel extends AbstractAttrsWizardStep<PlainSchemaTO> {
60  
61      private static final long serialVersionUID = -6664931684253730934L;
62  
63      protected final LinkedAccountTO linkedAccountTO;
64  
65      protected final UserTO userTO;
66  
67      protected final Set<Attr> fixedAttrs = new HashSet<>();
68  
69      protected final List<LinkedAccountPlainAttrProperty> accountPlainAttrProperties = new ArrayList<>();
70  
71      public <T extends AnyTO> LinkedAccountPlainAttrsPanel(
72              final EntityWrapper<LinkedAccountTO> modelObject,
73              final UserTO userTO,
74              final List<String> anyTypeClasses,
75              final List<String> whichPlainAttrs) throws IllegalArgumentException {
76  
77          super(userTO,
78                  AjaxWizard.Mode.EDIT,
79                  anyTypeClasses,
80                  whichPlainAttrs);
81  
82          this.linkedAccountTO = modelObject.getInnerObject();
83          this.fixedAttrs.addAll(this.linkedAccountTO.getPlainAttrs().stream().
84                  filter(attrTO -> checkIsReadonlyAttr(attrTO.getSchema())).
85                  collect(Collectors.toList()));
86          this.userTO = userTO;
87  
88          add(new Accordion("plainSchemas", List.of(new AbstractTab(
89                  new ResourceModel("attributes.accordion", "Plain Attributes")) {
90  
91              private static final long serialVersionUID = -7078941093668723016L;
92  
93              @Override
94              public WebMarkupContainer getPanel(final String panelId) {
95                  return new PlainSchemasOwn(panelId, schemas, attrs);
96              }
97          }), Model.of(0)).setOutputMarkupId(true));
98      }
99  
100     @Override
101     protected FormComponent<?> checkboxToggle(
102             final Attr attrTO,
103             final AbstractFieldPanel<?> panel,
104             final boolean isMultivalue) {
105 
106         LinkedAccountPlainAttrProperty property = accountPlainAttrProperties.stream().filter(
107                 existingProperty -> {
108                     return existingProperty.getSchema().equals(attrTO.getSchema());
109                 }).findFirst().orElseGet(() -> {
110                     LinkedAccountPlainAttrProperty newProperty = new LinkedAccountPlainAttrProperty();
111                     newProperty.setOverridable(linkedAccountTO.getPlainAttr(attrTO.getSchema()).isPresent());
112                     newProperty.setSchema(attrTO.getSchema());
113                     newProperty.getValues().addAll(attrTO.getValues());
114                     accountPlainAttrProperties.add(newProperty);
115                     return newProperty;
116                 });
117 
118         final BootstrapToggleConfig config = new BootstrapToggleConfig().
119                 withOnStyle(BootstrapToggleConfig.Style.success).
120                 withOffStyle(BootstrapToggleConfig.Style.danger).
121                 withSize(BootstrapToggleConfig.Size.mini);
122 
123         return new BootstrapToggle("externalAction", new PropertyModel<>(property, "overridable"), config) {
124 
125             private static final long serialVersionUID = -875219845189261873L;
126 
127             @Override
128             protected CheckBox newCheckBox(final String id, final IModel<Boolean> model) {
129                 final CheckBox checkBox = super.newCheckBox(id, model);
130                 checkBox.add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
131 
132                     private static final long serialVersionUID = -1107858522700306810L;
133 
134                     @Override
135                     protected void onUpdate(final AjaxRequestTarget target) {
136                         if (isMultivalue) {
137                             MultiFieldPanel.class.cast(panel).setFormReadOnly(!model.getObject());
138                         } else {
139                             FieldPanel.class.cast(panel).setReadOnly(!model.getObject());
140                         }
141 
142                         updateAccountPlainSchemas(property, model.getObject());
143                         target.add(panel);
144                     }
145                 });
146                 return checkBox;
147             }
148 
149             @Override
150             protected IModel<String> getOnLabel() {
151                 return Model.of("Override");
152             }
153 
154             @Override
155             protected IModel<String> getOffLabel() {
156                 return Model.of("Override?");
157             }
158 
159             @Override
160             protected void onComponentTag(final ComponentTag tag) {
161                 super.onComponentTag(tag);
162                 tag.append("class", "overridable", " ");
163             }
164         };
165     }
166 
167     private void updateAccountPlainSchemas(final LinkedAccountPlainAttrProperty property, final Boolean modelObject) {
168         Set<Attr> withoutCurrentSchema = new HashSet<>(linkedAccountTO.getPlainAttrs().stream().
169                 filter(attrTO -> !attrTO.getSchema().equals(property.getSchema())
170                 && checkIsReadonlyAttr(attrTO.getSchema())).
171                 collect(Collectors.toSet()));
172         linkedAccountTO.getPlainAttrs().clear();
173         linkedAccountTO.getPlainAttrs().addAll(withoutCurrentSchema);
174         if (modelObject) {
175             linkedAccountTO.getPlainAttrs().add(
176                     fixedAttrs.stream().filter(attrTO -> attrTO.getSchema().equals(property.getSchema())).findFirst().
177                             orElseGet(() -> new Attr.Builder(property.getSchema()).
178                             values(property.getValues()).build()));
179         }
180     }
181 
182     @Override
183     protected SchemaType getSchemaType() {
184         return SchemaType.PLAIN;
185     }
186 
187     @Override
188     protected void setAttrs() {
189         List<Attr> attrs = new ArrayList<>();
190         List<PlainSchemaTO> notReadonlyValues = schemas.values().stream().
191                 filter(schema -> checkIsReadonlyAttr(schema.getKey())).
192                 collect(Collectors.toList());
193         setFixedAttr(notReadonlyValues);
194         Map<String, Attr> attrMap = EntityTOUtils.buildAttrMap(fixedAttrs);
195 
196         attrs.addAll(notReadonlyValues.stream().
197                 map(schema -> {
198                     Attr attrTO = new Attr();
199                     attrTO.setSchema(schema.getKey());
200                     if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
201                         attrTO.getValues().add("");
202                     } else {
203                         attrTO = attrMap.get(schema.getKey());
204                     }
205                     return attrTO;
206                 }).
207                 collect(Collectors.toList()));
208 
209         fixedAttrs.clear();
210         fixedAttrs.addAll(attrs);
211     }
212 
213     @Override
214     protected List<Attr> getAttrsFromTO() {
215         return fixedAttrs.stream().sorted(attrComparator).collect(Collectors.toList());
216     }
217 
218     private void setFixedAttr(final Collection<PlainSchemaTO> values) {
219         values.forEach(schema -> linkedAccountTO.getPlainAttr(schema.getKey()).ifPresentOrElse(
220                 fixedAttrs::add,
221                 () -> userTO.getPlainAttr(schema.getKey()).ifPresent(fixedAttrs::add)));
222     }
223 
224     private boolean checkIsReadonlyAttr(final String schema) {
225         return schemas.isEmpty() ? true : !schemas.get(schema).isReadonly();
226     }
227 
228     private class PlainSchemasOwn extends PlainSchemas<List<Attr>> {
229 
230         private static final long serialVersionUID = -4730563859116024676L;
231 
232         PlainSchemasOwn(
233                 final String id,
234                 final Map<String, PlainSchemaTO> schemas,
235                 final IModel<List<Attr>> attrTOs) {
236 
237             super(id);
238 
239             add(new ListView<>("schemas", attrTOs) {
240 
241                 private static final long serialVersionUID = 9101744072914090143L;
242 
243                 @Override
244                 protected void populateItem(final ListItem<Attr> item) {
245                     Attr attrTO = item.getModelObject();
246                     final boolean isMultivalue = schemas.get(attrTO.getSchema()).isMultivalue();
247 
248                     AbstractFieldPanel<?> panel = setPanel(
249                             schemas,
250                             item,
251                             linkedAccountTO.getPlainAttr(attrTO.getSchema()).isEmpty());
252 
253                     panel.showExternAction(checkboxToggle(attrTO, panel, isMultivalue));
254                 }
255             });
256         }
257     }
258 }