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