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.mapping;
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.Comparator;
26  import java.util.List;
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
29  import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
30  import org.apache.syncope.client.console.widgets.ItemTransformerWidget;
31  import org.apache.syncope.client.console.widgets.JEXLTransformerWidget;
32  import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
33  import org.apache.syncope.client.ui.commons.Constants;
34  import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
35  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
36  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
37  import org.apache.syncope.common.lib.to.Item;
38  import org.apache.syncope.common.lib.types.MappingPurpose;
39  import org.apache.wicket.ajax.AjaxRequestTarget;
40  import org.apache.wicket.ajax.markup.html.form.AjaxButton;
41  import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
42  import org.apache.wicket.markup.html.WebMarkupContainer;
43  import org.apache.wicket.markup.html.basic.Label;
44  import org.apache.wicket.markup.html.list.ListItem;
45  import org.apache.wicket.markup.html.list.ListView;
46  import org.apache.wicket.markup.html.panel.Panel;
47  import org.apache.wicket.model.IModel;
48  import org.apache.wicket.model.Model;
49  import org.apache.wicket.model.PropertyModel;
50  import org.apache.wicket.model.ResourceModel;
51  
52  public abstract class AbstractMappingPanel extends Panel {
53  
54      private static final long serialVersionUID = -8295587900937040104L;
55  
56      protected static final Comparator<Item> ITEM_COMPARATOR = (left, right) -> {
57          int compared;
58          if (left == null && right == null) {
59              compared = 0;
60          } else if (left == null) {
61              compared = 1;
62          } else if (right == null) {
63              compared = -1;
64          } else if (left.isConnObjectKey()) {
65              compared = -1;
66          } else if (right.isConnObjectKey()) {
67              compared = 1;
68          } else if (left.isPassword()) {
69              compared = -1;
70          } else if (right.isPassword()) {
71              compared = 1;
72          } else if (left.getPurpose() == MappingPurpose.BOTH && right.getPurpose() != MappingPurpose.BOTH) {
73              compared = -1;
74          } else if (left.getPurpose() != MappingPurpose.BOTH && right.getPurpose() == MappingPurpose.BOTH) {
75              compared = 1;
76          } else if (left.getPurpose() == MappingPurpose.PROPAGATION
77                  && (right.getPurpose() == MappingPurpose.PULL
78                  || right.getPurpose() == MappingPurpose.NONE)) {
79              compared = -1;
80          } else if (left.getPurpose() == MappingPurpose.PULL
81                  && right.getPurpose() == MappingPurpose.PROPAGATION) {
82              compared = 1;
83          } else if (left.getPurpose() == MappingPurpose.PULL
84                  && right.getPurpose() == MappingPurpose.NONE) {
85              compared = -1;
86          } else if (left.getPurpose() == MappingPurpose.NONE
87                  && right.getPurpose() != MappingPurpose.NONE) {
88              compared = 1;
89          } else {
90              compared = left.getIntAttrName().compareTo(right.getIntAttrName());
91          }
92          return compared;
93      };
94  
95      protected final Label connObjectKeyLabel;
96  
97      protected final Label passwordLabel;
98  
99      protected final Label purposeLabel;
100 
101     protected final Label intAttrNameInfo;
102 
103     protected final WebMarkupContainer mandatoryHeader;
104 
105     /**
106      * Add mapping button.
107      */
108     protected final AjaxButton addMappingBtn;
109 
110     /**
111      * All mappings.
112      */
113     protected final ListView<Item> mappings;
114 
115     /**
116      * Mapping container.
117      */
118     protected final WebMarkupContainer mappingContainer;
119 
120     public AbstractMappingPanel(
121             final String id,
122             final ItemTransformersTogglePanel itemTransformers,
123             final JEXLTransformersTogglePanel jexlTransformers,
124             final IModel<List<Item>> model,
125             final boolean addMappingBtnVisible,
126             final MappingPurpose defaultPurpose) {
127 
128         super(id);
129         setOutputMarkupId(true);
130 
131         mappingContainer = new WebMarkupContainer("mappingContainer");
132         mappingContainer.setOutputMarkupId(true);
133         add(mappingContainer);
134 
135         mappingContainer.add(new Label("itemTransformersLabel", Model.of()).setVisible(itemTransformers != null));
136 
137         mappingContainer.add(new Label("jexlTransformersLabel", Model.of()).setVisible(jexlTransformers != null));
138 
139         connObjectKeyLabel = new Label("connObjectKeyLabel", new ResourceModel("connObjectKey"));
140         mappingContainer.add(connObjectKeyLabel);
141 
142         passwordLabel = new Label("passwordLabel", new ResourceModel("password"));
143         mappingContainer.add(passwordLabel);
144 
145         purposeLabel = new Label("purposeLabel", new ResourceModel("purpose"));
146         mappingContainer.add(purposeLabel);
147 
148         intAttrNameInfo = new Label("intAttrNameInfo", Model.of());
149         intAttrNameInfo.add(new PopoverBehavior(
150                 Model.of(),
151                 Model.of(getString("intAttrNameInfo.help")
152                         + "<code>groups[groupName].attribute</code>, "
153                         + "<code>users[userName].attribute</code>, "
154                         + "<code>anyObjects[anyObjectName].attribute</code>, "
155                         + "<code>relationships[relationshipType][anyType].attribute</code> or "
156                         + "<code>memberships[groupName].attribute</code> or "
157                         + "<code>privileges[applicationKey]</code>"),
158                 new PopoverConfig().withHtml(true).withPlacement(TooltipConfig.Placement.right)) {
159 
160             private static final long serialVersionUID = -7867802555691605021L;
161 
162             @Override
163             protected String createRelAttribute() {
164                 return "intAttrNameInfo";
165             }
166         });
167         mappingContainer.add(intAttrNameInfo);
168 
169         mandatoryHeader = new WebMarkupContainer("mandatoryHeader");
170         mandatoryHeader.setOutputMarkupId(true);
171         mandatoryHeader.add(Constants.getJEXLPopover(this, TooltipConfig.Placement.bottom));
172         mappingContainer.add(mandatoryHeader);
173 
174         model.getObject().sort(ITEM_COMPARATOR);
175 
176         mappings = new ListView<>("mappings", model) {
177 
178             private static final long serialVersionUID = 4949588177564901031L;
179 
180             @Override
181             protected void populateItem(final ListItem<Item> item) {
182                 final Item itemTO = item.getModelObject();
183                 if (itemTO.getPurpose() == null) {
184                     itemTO.setPurpose(defaultPurpose);
185                 }
186 
187                 //--------------------------------
188                 // Internal attribute
189                 // -------------------------------
190                 AjaxTextFieldPanel intAttrName = new AjaxTextFieldPanel(
191                         "intAttrName",
192                         "intAttrName",
193                         new PropertyModel<>(itemTO, "intAttrName"),
194                         false);
195                 intAttrName.setChoices(List.of());
196                 intAttrName.setRequired(true).hideLabel();
197                 item.add(intAttrName);
198                 // -------------------------------
199 
200                 //--------------------------------
201                 // External attribute
202                 // -------------------------------
203                 AjaxTextFieldPanel extAttrName = new AjaxTextFieldPanel(
204                         "extAttrName",
205                         "extAttrName",
206                         new PropertyModel<>(itemTO, "extAttrName"));
207                 extAttrName.setChoices(getExtAttrNames().getObject());
208 
209                 boolean required = !itemTO.isPassword();
210                 extAttrName.setRequired(required).hideLabel();
211                 extAttrName.setEnabled(required);
212                 item.add(extAttrName);
213                 // -------------------------------
214 
215                 //--------------------------------
216                 // JEXL transformers
217                 // -------------------------------
218                 if (jexlTransformers == null) {
219                     item.add(new Label("jexlTransformers").setVisible(false));
220                 } else {
221                     item.add(new JEXLTransformerWidget(
222                             "jexlTransformers", itemTO, jexlTransformers).setRenderBodyOnly(true));
223                 }
224                 // -------------------------------
225 
226                 //--------------------------------
227                 // Mapping item transformers
228                 // -------------------------------
229                 if (itemTransformers == null) {
230                     item.add(new Label("itemTransformers").setVisible(false));
231 
232                 } else {
233                     item.add(new ItemTransformerWidget(
234                             "itemTransformers", itemTO, itemTransformers).setRenderBodyOnly(true));
235                 }
236                 // -------------------------------
237 
238                 //--------------------------------
239                 // Mandatory
240                 // -------------------------------
241                 AjaxTextFieldPanel mandatory = new AjaxTextFieldPanel(
242                         "mandatoryCondition",
243                         "mandatoryCondition",
244                         new PropertyModel<>(itemTO, "mandatoryCondition"));
245                 mandatory.hideLabel();
246                 mandatory.setChoices(List.of("true", "false"));
247                 mandatory.setEnabled(!itemTO.isConnObjectKey());
248                 item.add(mandatory);
249                 // -------------------------------
250 
251                 //--------------------------------
252                 // Connector object key
253                 // -------------------------------
254                 AjaxCheckBoxPanel connObjectKey = new AjaxCheckBoxPanel(
255                         "connObjectKey",
256                         "connObjectKey",
257                         new PropertyModel<>(itemTO, "connObjectKey"), false);
258                 connObjectKey.hideLabel();
259                 item.add(connObjectKey);
260                 // -------------------------------
261 
262                 //--------------------------------
263                 // Password
264                 // -------------------------------
265                 AjaxCheckBoxPanel password = new AjaxCheckBoxPanel(
266                         "password",
267                         "password",
268                         new PropertyModel<>(itemTO, "password"), false);
269                 item.add(password.hideLabel());
270                 // -------------------------------
271 
272                 //--------------------------------
273                 // Purpose
274                 // -------------------------------
275                 WebMarkupContainer purpose = new WebMarkupContainer("purpose");
276                 purpose.setOutputMarkupId(true);
277 
278                 MappingPurposePanel purposeActions = new MappingPurposePanel(
279                         "purposeActions", new PropertyModel<>(itemTO, "purpose"), purpose);
280                 purpose.add(purposeActions.setRenderBodyOnly(true));
281                 item.add(purpose);
282                 // -------------------------------
283 
284                 //--------------------------------
285                 // Remove
286                 // -------------------------------
287                 ActionsPanel<Serializable> actions = new ActionsPanel<>("toRemove", null);
288                 actions.add(new ActionLink<>() {
289 
290                     private static final long serialVersionUID = -3722207913631435501L;
291 
292                     @Override
293                     public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
294                         model.getObject().remove(item.getIndex());
295                         item.getParent().removeAll();
296                         target.add(AbstractMappingPanel.this);
297                     }
298                 }, ActionLink.ActionType.DELETE, StringUtils.EMPTY, true).hideLabel();
299                 item.add(actions);
300                 // -------------------------------
301 
302                 intAttrName.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
303 
304                     private static final long serialVersionUID = -1107858522700306810L;
305 
306                     @Override
307                     protected void onUpdate(final AjaxRequestTarget target) {
308                     }
309                 });
310 
311                 connObjectKey.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
312 
313                     private static final long serialVersionUID = -1107858522700306810L;
314 
315                     @Override
316                     protected void onUpdate(final AjaxRequestTarget target) {
317                         if (connObjectKey.getModelObject()) {
318                             itemTO.setMandatoryCondition("true");
319                             mandatory.setModelObject("true");
320                             mandatory.setEnabled(false);
321                         } else {
322                             itemTO.setMandatoryCondition("false");
323                             mandatory.setModelObject("false");
324                             mandatory.setEnabled(true);
325                         }
326                         target.add(mandatory);
327                     }
328                 });
329 
330                 password.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
331 
332                     private static final long serialVersionUID = -1107858522700306810L;
333 
334                     @Override
335                     protected void onUpdate(final AjaxRequestTarget target) {
336                         extAttrName.setEnabled(!password.getModelObject());
337                         extAttrName.setModelObject(password.getModelObject()
338                                 ? ConnIdSpecialName.PASSWORD : extAttrName.getModelObject());
339                         extAttrName.setRequired(!password.getModelObject());
340                         target.add(extAttrName);
341 
342                         setConnObjectKey(connObjectKey, password);
343                         target.add(connObjectKey);
344                     }
345                 });
346 
347                 setConnObjectKey(connObjectKey, password);
348                 setAttrNames(intAttrName);
349 
350                 if (hidePassword()) {
351                     password.setVisible(false);
352 
353                     // Changes required by clone ....
354                     extAttrName.setEnabled(true);
355                     if (itemTO.isPassword()) {
356                         // re-enable if and only if cloned object mapping item was a password
357                         intAttrName.setEnabled(true);
358                     }
359                     itemTO.setPassword(false);
360                 }
361 
362                 purpose.setVisible(!hidePurpose());
363 
364                 mandatory.setVisible(!hideMandatory());
365 
366                 connObjectKey.setVisible(!hideConnObjectKey());
367             }
368         };
369 
370         mappings.setReuseItems(true);
371         mappingContainer.add(mappings);
372 
373         addMappingBtn = new IndicatingAjaxButton("addMappingBtn") {
374 
375             private static final long serialVersionUID = -4804368561204623354L;
376 
377             @Override
378             protected void onSubmit(final AjaxRequestTarget target) {
379                 model.getObject().add(new Item());
380                 target.add(AbstractMappingPanel.this);
381             }
382         };
383         addMappingBtn.setDefaultFormProcessing(false);
384         addMappingBtn.setEnabled(addMappingBtnVisible);
385         mappingContainer.add(addMappingBtn);
386     }
387 
388     @Override
389     protected void onBeforeRender() {
390         super.onBeforeRender();
391 
392         passwordLabel.setVisible(!hidePassword());
393         purposeLabel.setVisible(!hidePurpose());
394         mandatoryHeader.setVisible(!hideMandatory());
395         connObjectKeyLabel.setVisible(!hideConnObjectKey());
396     }
397 
398     protected boolean hidePassword() {
399         return true;
400     }
401 
402     protected boolean hidePurpose() {
403         return false;
404     }
405 
406     protected boolean hideMandatory() {
407         return false;
408     }
409 
410     protected boolean hideConnObjectKey() {
411         return false;
412     }
413 
414     protected abstract IModel<List<String>> getExtAttrNames();
415 
416     /**
417      * Set attribute names for a drop down choice list.
418      *
419      * @param toBeUpdated drop down choice to be updated.
420      */
421     protected abstract void setAttrNames(AjaxTextFieldPanel toBeUpdated);
422 
423     /**
424      * Enable/Disable connObjectKey checkbox.
425      *
426      * @param connObjectKey connObjectKey checkbox.
427      * @param password password checkbox.
428      */
429     protected void setConnObjectKey(final AjaxCheckBoxPanel connObjectKey, final AjaxCheckBoxPanel password) {
430         if (password.getModelObject()) {
431             connObjectKey.setReadOnly(true);
432             connObjectKey.setModelObject(false);
433         } else {
434             connObjectKey.setReadOnly(false);
435         }
436     }
437 }