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;
20  
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Optional;
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.syncope.client.console.SyncopeConsoleSession;
27  import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
28  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
29  import org.apache.syncope.client.console.wizards.any.ResultPanel;
30  import org.apache.syncope.client.ui.commons.Constants;
31  import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
32  import org.apache.syncope.client.ui.commons.panels.NotificationPanel;
33  import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
34  import org.apache.syncope.client.ui.commons.wizards.AbstractWizardMgtPanel;
35  import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
36  import org.apache.syncope.client.ui.commons.wizards.ModalPanelBuilder;
37  import org.apache.wicket.Component;
38  import org.apache.wicket.MarkupContainer;
39  import org.apache.wicket.PageReference;
40  import org.apache.wicket.ajax.AjaxRequestTarget;
41  import org.apache.wicket.ajax.markup.html.AjaxLink;
42  import org.apache.wicket.event.Broadcast;
43  import org.apache.wicket.event.IEvent;
44  import org.apache.wicket.markup.html.WebMarkupContainer;
45  import org.apache.wicket.markup.html.basic.Label;
46  import org.apache.wicket.markup.html.list.ListItem;
47  import org.apache.wicket.markup.html.list.ListView;
48  import org.apache.wicket.markup.html.panel.Fragment;
49  import org.apache.wicket.markup.html.panel.Panel;
50  import org.apache.wicket.model.Model;
51  import org.apache.wicket.model.StringResourceModel;
52  
53  public abstract class WizardMgtPanel<T extends Serializable> extends AbstractWizardMgtPanel<T> {
54  
55      private static final long serialVersionUID = -4152438633429194882L;
56  
57      private boolean readOnly = false;
58  
59      protected final String actualId;
60  
61      private final WebMarkupContainer container;
62  
63      protected final Fragment initialFragment;
64  
65      protected final boolean wizardInModal;
66  
67      private boolean containerAutoRefresh = true;
68  
69      protected PageReference pageRef;
70  
71      protected final AjaxLink<?> addAjaxLink;
72  
73      protected Label utilityIcon;
74  
75      protected AjaxLink<?> utilityAjaxLink;
76  
77      protected ModalPanelBuilder<T> newItemPanelBuilder;
78  
79      protected NotificationPanel notificationPanel;
80  
81      protected boolean footerVisibility = false;
82  
83      protected boolean showResultPanel = false;
84  
85      private final List<Component> outerObjects = new ArrayList<>();
86  
87      protected final BaseModal<T> modal = new BaseModal<>(Constants.OUTER) {
88  
89          private static final long serialVersionUID = 389935548143327858L;
90  
91          @Override
92          protected void onConfigure() {
93              super.onConfigure();
94              setFooterVisible(footerVisibility);
95          }
96      };
97  
98      protected WizardMgtPanel(final String id) {
99          this(id, false);
100     }
101 
102     protected WizardMgtPanel(final String id, final boolean wizardInModal) {
103         super(id);
104         setOutputMarkupId(true);
105 
106         this.actualId = wizardInModal ? BaseModal.CONTENT_ID : WIZARD_ID;
107         this.wizardInModal = wizardInModal;
108 
109         outerObjects.add(modal);
110 
111         container = new WebMarkupContainer("container");
112         container.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
113         add(container);
114 
115         initialFragment = new Fragment("content", "default", this);
116         container.addOrReplace(initialFragment);
117 
118         addAjaxLink = new AjaxLink<T>("add") {
119 
120             private static final long serialVersionUID = -7978723352517770644L;
121 
122             @Override
123             public void onClick(final AjaxRequestTarget target) {
124                 send(WizardMgtPanel.this, Broadcast.BREADTH,
125                         new ActionLinksTogglePanel.ActionLinkToggleCloseEventPayload(target));
126                 send(WizardMgtPanel.this, Broadcast.EXACT,
127                         new AjaxWizard.NewItemActionEvent<>(null, target));
128             }
129         };
130 
131         addAjaxLink.setEnabled(false);
132         addAjaxLink.setVisible(false);
133         initialFragment.addOrReplace(addAjaxLink);
134 
135         utilityAjaxLink = new AjaxLink<T>("utility") {
136 
137             private static final long serialVersionUID = -7978723352517770644L;
138 
139             @Override
140             public void onClick(final AjaxRequestTarget target) {
141                 send(WizardMgtPanel.this, Broadcast.EXACT, new ExitEvent(target));
142             }
143         };
144 
145         utilityAjaxLink.setEnabled(false);
146         utilityAjaxLink.setVisible(false);
147         initialFragment.addOrReplace(utilityAjaxLink);
148 
149         utilityIcon = new Label("utilityIcon");
150         utilityAjaxLink.add(utilityIcon);
151 
152         add(new ListView<>("outerObjectsRepeater", outerObjects) {
153 
154             private static final long serialVersionUID = -9180479401817023838L;
155 
156             @Override
157             protected void populateItem(final ListItem<Component> item) {
158                 item.add(item.getModelObject());
159             }
160         });
161     }
162 
163     public String getActualId() {
164         return actualId;
165     }
166 
167     @Override
168     @SuppressWarnings("unchecked")
169     public void onEvent(final IEvent<?> event) {
170         if (event.getPayload() instanceof ExitEvent) {
171             AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
172             // default behaviour: change it catching the event if needed
173             modal.close(target);
174         } else if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
175             AjaxWizard.NewItemEvent<T> newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload());
176             Optional<AjaxRequestTarget> target = newItemEvent.getTarget();
177             T item = newItemEvent.getItem();
178 
179             boolean modalPanelAvailable = newItemEvent.getModalPanel() != null || newItemPanelBuilder != null;
180 
181             if (event.getPayload() instanceof AjaxWizard.NewItemActionEvent && modalPanelAvailable) {
182                 WizardModalPanel<?> modalPanel;
183                 if (newItemEvent.getModalPanel() == null && newItemPanelBuilder != null) {
184                     newItemPanelBuilder.setItem(item);
185 
186                     modalPanel = newItemPanelBuilder.build(
187                             actualId,
188                             ((AjaxWizard.NewItemActionEvent<T>) newItemEvent).getIndex(),
189                             item != null
190                                     ? isReadOnly()
191                                             ? AjaxWizard.Mode.READONLY
192                                             : AjaxWizard.Mode.EDIT
193                                     : AjaxWizard.Mode.CREATE);
194                 } else {
195                     modalPanel = newItemEvent.getModalPanel();
196                 }
197 
198                 if (wizardInModal) {
199                     modal.setFormModel(item);
200 
201                     target.ifPresent(t -> t.add(modal.setContent(modalPanel)));
202 
203                     modal.header(Optional.ofNullable(newItemEvent.getTitleModel()).
204                             orElse(new StringResourceModel(
205                                     String.format("any.%s", newItemEvent.getEventDescription()),
206                                     this,
207                                     Model.of(modalPanel.getItem()))));
208                     modal.show(true);
209                 } else {
210                     Fragment fragment = new Fragment("content", "wizard", WizardMgtPanel.this);
211 
212                     fragment.add(new Label(
213                             "title",
214                             Optional.ofNullable(newItemEvent.getTitleModel()).orElse(Model.of(StringUtils.EMPTY))));
215 
216                     fragment.add(Component.class.cast(modalPanel));
217                     container.addOrReplace(fragment);
218                 }
219 
220                 target.ifPresent(this::customActionCallback);
221             } else if (event.getPayload() instanceof AjaxWizard.NewItemCancelEvent) {
222                 if (wizardInModal) {
223                     target.ifPresent(modal::close);
224                 } else {
225                     container.addOrReplace(initialFragment);
226                 }
227 
228                 target.ifPresent(this::customActionOnCancelCallback);
229             } else if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
230                 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
231                 target.ifPresent(t -> ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(t));
232 
233                 if (wizardInModal && showResultPanel) {
234                     modal.setContent(new ResultPanel<>(
235                             item,
236                             AjaxWizard.NewItemFinishEvent.class.cast(newItemEvent).getResult()) {
237 
238                         private static final long serialVersionUID = -2630573849050255233L;
239 
240                         @Override
241                         protected void closeAction(final AjaxRequestTarget target) {
242                             modal.close(target);
243                         }
244 
245                         @Override
246                         protected Panel customResultBody(
247                                 final String panelId,
248                                 final T item,
249                                 final Serializable result) {
250 
251                             return WizardMgtPanel.this.customResultBody(panelId, item, result);
252                         }
253                     });
254                     target.ifPresent(t -> t.add(modal.getForm()));
255                 } else if (wizardInModal) {
256                     target.ifPresent(modal::close);
257                 } else {
258                     container.addOrReplace(initialFragment);
259                 }
260 
261                 target.ifPresent(this::customActionOnFinishCallback);
262             }
263 
264             if (containerAutoRefresh) {
265                 target.ifPresent(t -> t.add(container));
266             }
267         }
268         super.onEvent(event);
269     }
270 
271     protected final WizardMgtPanel<T> disableContainerAutoRefresh() {
272         containerAutoRefresh = false;
273         return this;
274     }
275 
276     /*
277      * Override this method to specify your custom result body panel.
278      */
279     protected Panel customResultBody(final String panelId, final T item, final Serializable result) {
280         return new Panel(panelId) {
281 
282             private static final long serialVersionUID = 5538299138211283825L;
283 
284         };
285     }
286 
287     /**
288      * Show utility button sending ExitEvent payload by default.
289      *
290      * @return the current instance.
291      */
292     protected final WizardMgtPanel<T> enableUtilityButton() {
293         utilityAjaxLink.setEnabled(true);
294         utilityAjaxLink.setVisible(true);
295         return this;
296     }
297 
298     /**
299      * Add object inside the main container.
300      *
301      * @param childs components to be added.
302      * @return the current panel instance.
303      */
304     public MarkupContainer addInnerObject(final Component... childs) {
305         return initialFragment.add(childs);
306     }
307 
308     /**
309      * Add or replace object inside the main container.
310      *
311      * @param childs components to be added.
312      * @return the current panel instance.
313      */
314     public MarkupContainer addOrReplaceInnerObject(final Component... childs) {
315         return initialFragment.addOrReplace(childs);
316     }
317 
318     /**
319      * Add object outside the main container.
320      * Use this method just to be not influenced by specific inner object css'.
321      * Be sure to provide {@code outer} as id.
322      *
323      * @param childs components to be added.
324      * @return the current panel instance.
325      */
326     public final WizardMgtPanel<T> addOuterObject(final Component... childs) {
327         outerObjects.addAll(List.of(childs));
328         return this;
329     }
330 
331     public <B extends ModalPanelBuilder<T>> WizardMgtPanel<T> setPageRef(final PageReference pageRef) {
332         this.pageRef = pageRef;
333         return this;
334     }
335 
336     public <B extends ModalPanelBuilder<T>> WizardMgtPanel<T> setShowResultPanel(final boolean showResultPanel) {
337         this.showResultPanel = showResultPanel;
338         return this;
339     }
340 
341     protected <B extends ModalPanelBuilder<T>> WizardMgtPanel<T> addNewItemPanelBuilder(
342             final B panelBuilder, final boolean newItemDefaultButtonEnabled) {
343 
344         this.newItemPanelBuilder = panelBuilder;
345 
346         if (this.newItemPanelBuilder != null) {
347             addAjaxLink.setEnabled(newItemDefaultButtonEnabled);
348             addAjaxLink.setVisible(newItemDefaultButtonEnabled);
349             this.newItemPanelBuilder.setEventSink(WizardMgtPanel.this);
350         }
351 
352         return this;
353     }
354 
355     protected WizardMgtPanel<T> addNotificationPanel(final NotificationPanel notificationPanel) {
356         this.notificationPanel = notificationPanel;
357         return this;
358     }
359 
360     public WizardMgtPanel<T> setFooterVisibility(final boolean footerVisibility) {
361         this.footerVisibility = footerVisibility;
362         return this;
363     }
364 
365     /**
366      * Set window close callback for the given modal.
367      *
368      * @param modal target modal.
369      */
370     protected void setWindowClosedReloadCallback(final BaseModal<?> modal) {
371         modal.setWindowClosedCallback(target -> modal.show(false));
372     }
373 
374     /**
375      * Custom action to perform on create/edit action callback.
376      *
377      * @param target Ajax request target.
378      */
379     protected void customActionCallback(final AjaxRequestTarget target) {
380     }
381 
382     /**
383      * Custom action to perform on close callback on finish event.
384      *
385      * @param target Ajax request target.
386      */
387     protected void customActionOnFinishCallback(final AjaxRequestTarget target) {
388     }
389 
390     /**
391      * Custom action to perform on close callback on cancel event.
392      *
393      * @param target Ajax request target.
394      */
395     protected void customActionOnCancelCallback(final AjaxRequestTarget target) {
396     }
397 
398     /**
399      * PanelInWizard abstract builder.
400      *
401      * @param <T> list item reference type.
402      */
403     public abstract static class Builder<T extends Serializable> implements Serializable {
404 
405         private static final long serialVersionUID = 1908836274665387084L;
406 
407         protected final PageReference pageRef;
408 
409         private ModalPanelBuilder<T> newItemPanelBuilder;
410 
411         private boolean newItemDefaultButtonEnabled = true;
412 
413         private NotificationPanel notificationPanel;
414 
415         private boolean showResultPage = false;
416 
417         private boolean wizardInModal = false;
418 
419         protected Builder(final PageReference pageRef) {
420             this.pageRef = pageRef;
421         }
422 
423         protected abstract WizardMgtPanel<T> newInstance(String id, boolean wizardInModal);
424 
425         /**
426          * Builds a list view.
427          *
428          * @param id component id.
429          * @return List view.
430          */
431         public WizardMgtPanel<T> build(final String id) {
432             return newInstance(id, wizardInModal).
433                     setPageRef(this.pageRef).
434                     setShowResultPanel(this.showResultPage).
435                     addNewItemPanelBuilder(this.newItemPanelBuilder, this.newItemDefaultButtonEnabled).
436                     addNotificationPanel(this.notificationPanel);
437         }
438 
439         public void setShowResultPage(final boolean showResultPage) {
440             this.showResultPage = showResultPage;
441         }
442 
443         public Builder<T> addNewItemPanelBuilder(final ModalPanelBuilder<T> panelBuilder) {
444             this.newItemPanelBuilder = panelBuilder;
445             return this;
446         }
447 
448         /**
449          * Adds new item panel builder.
450          *
451          * @param panelBuilder new item panel builder.
452          * @param newItemDefaultButtonEnabled enable default button to adda new item.
453          * @return the current builder.
454          */
455         public Builder<T> addNewItemPanelBuilder(
456                 final ModalPanelBuilder<T> panelBuilder, final boolean newItemDefaultButtonEnabled) {
457 
458             this.newItemDefaultButtonEnabled = newItemDefaultButtonEnabled;
459             return addNewItemPanelBuilder(panelBuilder);
460         }
461 
462         /**
463          * Adds new item panel builder and enables default button to adda new item.
464          *
465          * @param notificationPanel new item panel builder.
466          * @return the current builder.
467          */
468         public Builder<T> addNotificationPanel(final NotificationPanel notificationPanel) {
469             this.notificationPanel = notificationPanel;
470             return this;
471         }
472 
473         /**
474          * Specifies to open an edit item wizard into a new modal page.
475          *
476          * @param wizardInModal TRUE to request to open wizard in a new modal.
477          * @return the current builder.
478          */
479         public Builder<T> setWizardInModal(final boolean wizardInModal) {
480             this.wizardInModal = wizardInModal;
481             return this;
482         }
483     }
484 
485     public static class ExitEvent {
486 
487         private final AjaxRequestTarget target;
488 
489         public ExitEvent(final AjaxRequestTarget target) {
490             this.target = target;
491         }
492 
493         public AjaxRequestTarget getTarget() {
494             return target;
495         }
496     }
497 
498     public boolean isReadOnly() {
499         return readOnly;
500     }
501 
502     public void setReadOnly(final boolean readOnly) {
503         this.readOnly = readOnly;
504     }
505 }