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.panels;
20  
21  import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.List;
26  import java.util.Optional;
27  import org.apache.syncope.client.console.batch.BatchContent;
28  import org.apache.syncope.client.console.batch.BatchModal;
29  import org.apache.syncope.client.console.pages.BasePage;
30  import org.apache.syncope.client.console.panels.DirectoryPanel.EventDataWrapper;
31  import org.apache.syncope.client.console.rest.BaseRestClient;
32  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.AjaxFallbackDataTable;
33  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CheckGroupColumn;
34  import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
35  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
36  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
37  import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
38  import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormChoiceComponentUpdatingBehavior;
39  import org.apache.syncope.client.ui.commons.rest.RestClient;
40  import org.apache.wicket.AttributeModifier;
41  import org.apache.wicket.PageReference;
42  import org.apache.wicket.ajax.AjaxRequestTarget;
43  import org.apache.wicket.event.Broadcast;
44  import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
45  import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
46  import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
47  import org.apache.wicket.markup.html.WebMarkupContainer;
48  import org.apache.wicket.markup.html.form.CheckGroup;
49  import org.apache.wicket.markup.html.form.CheckGroupSelector;
50  import org.apache.wicket.markup.html.form.Form;
51  import org.apache.wicket.markup.html.panel.Fragment;
52  import org.apache.wicket.model.IModel;
53  import org.apache.wicket.model.ResourceModel;
54  
55  public final class AjaxDataTablePanel<T extends Serializable, S> extends DataTablePanel<T, S> {
56  
57      private static final long serialVersionUID = -7264400471578272966L;
58  
59      public static class Builder<T extends Serializable, S> implements Serializable {
60  
61          private static final long serialVersionUID = 8876232177473972722L;
62  
63          private boolean checkBoxEnabled = true;
64  
65          private final List<IColumn<T, S>> columns = new ArrayList<>();
66  
67          private final ISortableDataProvider<T, S> dataProvider;
68  
69          private int rowsPerPage = 10;
70  
71          private final Collection<ActionLink.ActionType> batches = new ArrayList<>();
72  
73          private RestClient batchExecutor;
74  
75          private String itemKeyField;
76  
77          private final PageReference pageRef;
78  
79          private WebMarkupContainer container;
80  
81          private MultilevelPanel multiLevelPanel;
82  
83          public Builder(final ISortableDataProvider<T, S> provider, final PageReference pageRef) {
84              this.dataProvider = provider;
85              this.pageRef = pageRef;
86          }
87  
88          public AjaxDataTablePanel<T, S> build(final String id) {
89              return new AjaxDataTablePanel<>(id, this);
90          }
91  
92          public Builder<T, S> setContainer(final WebMarkupContainer container) {
93              this.container = container;
94              return this;
95          }
96  
97          public Builder<T, S> addBatch(final ActionLink.ActionType actionType) {
98              batches.add(actionType);
99              return this;
100         }
101 
102         public Builder<T, S> setBatchExecutor(final BaseRestClient batchExecutor) {
103             this.batchExecutor = batchExecutor;
104             return this;
105         }
106 
107         public Builder<T, S> setItemKeyField(final String itemKeyField) {
108             this.itemKeyField = itemKeyField;
109             return this;
110         }
111 
112         public Builder<T, S> setBatches(
113                 final Collection<ActionLink.ActionType> batches,
114                 final RestClient batchExecutor,
115                 final String itemKeyField) {
116 
117             this.batches.clear();
118             if (batches != null) {
119                 this.batches.addAll(batches);
120             }
121             this.batchExecutor = batchExecutor;
122             this.itemKeyField = itemKeyField;
123             return this;
124         }
125 
126         public Builder<T, S> addColumn(final IColumn<T, S> column) {
127             columns.add(column);
128             return this;
129         }
130 
131         public Builder<T, S> setColumns(final List<IColumn<T, S>> columns) {
132             this.columns.clear();
133             if (columns != null) {
134                 this.columns.addAll(columns);
135             }
136             return this;
137         }
138 
139         public Builder<T, S> setRowsPerPage(final int rowsPerPage) {
140             this.rowsPerPage = rowsPerPage;
141             return this;
142         }
143 
144         public Builder<T, S> disableCheckBoxes() {
145             this.checkBoxEnabled = false;
146             return this;
147         }
148 
149         private boolean isBatchEnabled() {
150             return checkBoxEnabled && batchExecutor != null && !batches.isEmpty();
151         }
152 
153         public void setMultiLevelPanel(final MultilevelPanel multiLevelPanel) {
154             this.multiLevelPanel = multiLevelPanel;
155         }
156 
157         protected ActionsPanel<T> getActions(final IModel<T> model) {
158             return null;
159         }
160 
161         protected ActionLinksTogglePanel<T> getTogglePanel() {
162             return null;
163         }
164     }
165 
166     protected final BaseModal<T> batchModal;
167 
168     private AjaxDataTablePanel(final String id, final Builder<T, S> builder) {
169         super(id);
170 
171         batchModal = new BaseModal<>("batchModal");
172         batchModal.size(Modal.Size.Extra_large);
173         add(batchModal);
174 
175         batchModal.setWindowClosedCallback(target -> {
176             batchModal.show(false);
177 
178             EventDataWrapper data = new EventDataWrapper();
179             data.setTarget(target);
180             data.setRows(builder.rowsPerPage);
181 
182             send(builder.pageRef.getPage(), Broadcast.BREADTH, data);
183             Optional.ofNullable((BasePage) findPage()).
184                     ifPresent(page -> page.getNotificationPanel().refresh(target));
185         });
186 
187         Fragment fragment = new Fragment("tablePanel", "batchAvailable", this);
188         add(fragment);
189 
190         Form<T> groupForm = new Form<>("groupForm");
191         fragment.add(groupForm);
192 
193         group = new CheckGroup<>("checkgroup", model);
194         group.add(new IndicatorAjaxFormChoiceComponentUpdatingBehavior() {
195 
196             private static final long serialVersionUID = -151291731388673682L;
197 
198             @Override
199             protected void onUpdate(final AjaxRequestTarget target) {
200                 group.visitChildren(CheckGroupSelector.class, (selector, ivisit) -> {
201                     target.focusComponent(selector);
202                     ivisit.stop();
203                 });
204             }
205         });
206         groupForm.add(group);
207 
208         if (builder.checkBoxEnabled) {
209             builder.columns.add(0, new CheckGroupColumn<>(group));
210         }
211 
212         dataTable = new AjaxFallbackDataTable<>(
213                 "dataTable", builder.columns, builder.dataProvider, builder.rowsPerPage, builder.container) {
214 
215             private static final long serialVersionUID = -7370603907251344224L;
216 
217             @Override
218             protected ActionsPanel<T> getActions(final IModel<T> model) {
219                 return builder.getActions(model);
220             }
221 
222             @Override
223             protected ActionLinksTogglePanel<T> getTogglePanel() {
224                 return builder.getTogglePanel();
225             }
226 
227         };
228 
229         dataTable.add(new AttributeModifier("class", "table table-bordered table-hover dataTable"));
230 
231         group.add(dataTable);
232 
233         fragment.add(new IndicatingAjaxButton("batchLink", groupForm) {
234 
235             private static final long serialVersionUID = 382302811235019988L;
236 
237             @Override
238             protected void onSubmit(final AjaxRequestTarget target) {
239                 // send event to close eventually opened actions toggle panel
240                 Optional.ofNullable(builder.getTogglePanel()).ifPresent(p -> p.close(target));
241 
242                 if (builder.multiLevelPanel == null) {
243                     batchModal.header(new ResourceModel("batch"));
244                     batchModal.changeCloseButtonLabel(getString("cancel", null, "Cancel"), target);
245 
246                     target.add(batchModal.setContent(new BatchModal<>(
247                             batchModal,
248                             builder.pageRef,
249                             new ArrayList<>(group.getModelObject()),
250                             builder.columns.size() == 1
251                             ? builder.columns
252                             // serialization problem with sublist only
253                             : new ArrayList<>(builder.columns.subList(1, builder.columns.size())),
254                             builder.batches,
255                             builder.batchExecutor,
256                             builder.itemKeyField)));
257 
258                     batchModal.show(true);
259                 } else {
260                     builder.multiLevelPanel.next(getString("batch"),
261                             new BatchContent<>(
262                                     new ArrayList<>(group.getModelObject()),
263                                     builder.columns.size() == 1
264                                     ? builder.columns
265                                     // serialization problem with sublist only
266                                     : new ArrayList<>(builder.columns.subList(1, builder.columns.size())),
267                                     builder.batches,
268                                     builder.batchExecutor,
269                                     builder.itemKeyField),
270                             target);
271                 }
272                 group.setModelObject(List.of());
273                 target.add(group);
274             }
275         }.setEnabled(builder.isBatchEnabled()).setVisible(builder.isBatchEnabled()));
276     }
277 }