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.reports;
20  
21  import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
22  import java.time.Duration;
23  import java.time.temporal.ChronoUnit;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.List;
28  import org.apache.commons.lang3.SerializationUtils;
29  import org.apache.syncope.client.console.SyncopeConsoleSession;
30  import org.apache.syncope.client.console.commons.DirectoryDataProvider;
31  import org.apache.syncope.client.console.commons.IdRepoConstants;
32  import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
33  import org.apache.syncope.client.console.pages.BasePage;
34  import org.apache.syncope.client.console.panels.DirectoryPanel;
35  import org.apache.syncope.client.console.panels.MultilevelPanel;
36  import org.apache.syncope.client.console.rest.ImplementationRestClient;
37  import org.apache.syncope.client.console.rest.ReportRestClient;
38  import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
39  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
40  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
41  import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
42  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
43  import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
44  import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
45  import org.apache.syncope.client.console.widgets.JobActionPanel;
46  import org.apache.syncope.client.ui.commons.Constants;
47  import org.apache.syncope.client.ui.commons.MIMETypesLoader;
48  import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
49  import org.apache.syncope.common.lib.SyncopeClientException;
50  import org.apache.syncope.common.lib.to.JobTO;
51  import org.apache.syncope.common.lib.to.ReportTO;
52  import org.apache.syncope.common.lib.types.IdRepoEntitlement;
53  import org.apache.wicket.Component;
54  import org.apache.wicket.PageReference;
55  import org.apache.wicket.ajax.AjaxRequestTarget;
56  import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
57  import org.apache.wicket.event.Broadcast;
58  import org.apache.wicket.event.IEvent;
59  import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
60  import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
61  import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
62  import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
63  import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
64  import org.apache.wicket.markup.html.WebPage;
65  import org.apache.wicket.markup.html.basic.Label;
66  import org.apache.wicket.markup.repeater.Item;
67  import org.apache.wicket.model.CompoundPropertyModel;
68  import org.apache.wicket.model.IModel;
69  import org.apache.wicket.model.Model;
70  import org.apache.wicket.model.StringResourceModel;
71  import org.apache.wicket.spring.injection.annot.SpringBean;
72  
73  /**
74   * Reports page.
75   */
76  public abstract class ReportDirectoryPanel
77          extends DirectoryPanel<ReportTO, ReportTO, DirectoryDataProvider<ReportTO>, ReportRestClient> {
78  
79      private static final long serialVersionUID = 4984337552918213290L;
80  
81      protected final ReportStartAtTogglePanel startAt;
82  
83      @SpringBean
84      protected ImplementationRestClient implementationRestClient;
85  
86      @SpringBean
87      protected ReportRestClient reportRestClient;
88  
89      @SpringBean
90      protected MIMETypesLoader mimeTypesLoader;
91  
92      protected ReportDirectoryPanel(final ReportRestClient restClient, final PageReference pageRef) {
93          super(MultilevelPanel.FIRST_LEVEL_ID, restClient, pageRef, true);
94  
95          addNewItemPanelBuilder(new ReportWizardBuilder(
96                  new ReportTO(), implementationRestClient, reportRestClient, mimeTypesLoader, pageRef), true);
97          MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, IdRepoEntitlement.REPORT_CREATE);
98  
99          modal.size(Modal.Size.Large);
100         initResultTable();
101 
102         container.add(new IndicatorAjaxTimerBehavior(Duration.of(10, ChronoUnit.SECONDS)) {
103 
104             private static final long serialVersionUID = -4661303265651934868L;
105 
106             @Override
107             protected void onTimer(final AjaxRequestTarget target) {
108                 container.modelChanged();
109                 target.add(container);
110             }
111         });
112 
113         startAt = new ReportStartAtTogglePanel(container, pageRef);
114         addInnerObject(startAt);
115     }
116 
117     @Override
118     protected List<IColumn<ReportTO, String>> getColumns() {
119         List<IColumn<ReportTO, String>> columns = new ArrayList<>();
120 
121         columns.add(new KeyPropertyColumn<>(
122                 new StringResourceModel(Constants.KEY_FIELD_NAME, this), Constants.KEY_FIELD_NAME));
123         columns.add(new PropertyColumn<>(new StringResourceModel(Constants.NAME_FIELD_NAME, this),
124                 Constants.NAME_FIELD_NAME, Constants.NAME_FIELD_NAME));
125 
126         columns.add(new PropertyColumn<>(
127                 new StringResourceModel("mimeType", this), "mimeType", "mimeType"));
128 
129         columns.add(new DatePropertyColumn<>(
130                 new StringResourceModel("lastExec", this), null, "lastExec"));
131 
132         columns.add(new DatePropertyColumn<>(
133                 new StringResourceModel("nextExec", this), null, "nextExec"));
134 
135         columns.add(new DatePropertyColumn<>(
136                 new StringResourceModel("start", this), "start", "start"));
137 
138         columns.add(new DatePropertyColumn<>(
139                 new StringResourceModel("end", this), "end", "end"));
140 
141         columns.add(new PropertyColumn<>(
142                 new StringResourceModel("latestExecStatus", this), "latestExecStatus", "latestExecStatus"));
143 
144         columns.add(new BooleanPropertyColumn<>(
145                 new StringResourceModel("active", this), "active", "active"));
146 
147         columns.add(new AbstractColumn<>(new Model<>(""), "running") {
148 
149             private static final long serialVersionUID = 4209532514416998046L;
150 
151             @Override
152             public void populateItem(
153                     final Item<ICellPopulator<ReportTO>> cellItem,
154                     final String componentId,
155                     final IModel<ReportTO> rowModel) {
156 
157                 Component panel;
158                 try {
159                     JobTO jobTO = restClient.getJob(rowModel.getObject().getKey());
160                     panel = new JobActionPanel(componentId, jobTO, false, ReportDirectoryPanel.this);
161                     MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
162                             String.format("%s,%s",
163                                     IdRepoEntitlement.REPORT_EXECUTE,
164                                     IdRepoEntitlement.REPORT_UPDATE));
165                 } catch (Exception e) {
166                     LOG.error("Could not get job for report {}", rowModel.getObject().getKey(), e);
167                     panel = new Label(componentId, Model.of());
168                 }
169                 cellItem.add(panel);
170             }
171 
172             @Override
173             public String getCssClass() {
174                 return "running-col";
175             }
176         });
177 
178         return columns;
179     }
180 
181     @Override
182     public void onEvent(final IEvent<?> event) {
183         if (event.getPayload() instanceof JobActionPanel.JobActionPayload) {
184             container.modelChanged();
185             JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container);
186         } else {
187             super.onEvent(event);
188         }
189     }
190 
191     @Override
192     public ActionsPanel<ReportTO> getActions(final IModel<ReportTO> model) {
193         ActionsPanel<ReportTO> panel = super.getActions(model);
194 
195         panel.add(new ActionLink<>() {
196 
197             private static final long serialVersionUID = -3722207913631435501L;
198 
199             @Override
200             public void onClick(final AjaxRequestTarget target, final ReportTO ignore) {
201                 viewReportExecs(model.getObject(), target);
202             }
203         }, ActionLink.ActionType.VIEW_EXECUTIONS, IdRepoEntitlement.REPORT_READ);
204 
205         panel.add(new ActionLink<>() {
206 
207             private static final long serialVersionUID = -3722207913631435501L;
208 
209             @Override
210             public void onClick(final AjaxRequestTarget target, final ReportTO ignore) {
211                 send(ReportDirectoryPanel.this, Broadcast.EXACT,
212                         new AjaxWizard.EditItemActionEvent<>(restClient.read(model.getObject().getKey()), target));
213             }
214         }, ActionLink.ActionType.EDIT, IdRepoEntitlement.REPORT_UPDATE);
215 
216         panel.add(new ActionLink<>() {
217 
218             private static final long serialVersionUID = -3722207913631435501L;
219 
220             @Override
221             public void onClick(final AjaxRequestTarget target, final ReportTO ignore) {
222                 final ReportTO clone = SerializationUtils.clone(model.getObject());
223                 clone.setKey(null);
224                 send(ReportDirectoryPanel.this, Broadcast.EXACT,
225                         new AjaxWizard.EditItemActionEvent<>(clone, target));
226             }
227         }, ActionLink.ActionType.CLONE, IdRepoEntitlement.REPORT_CREATE);
228 
229         panel.add(new ActionLink<>() {
230 
231             private static final long serialVersionUID = -3722207913631435501L;
232 
233             @Override
234             public void onClick(final AjaxRequestTarget target, final ReportTO ignore) {
235                 startAt.setExecutionDetail(
236                         model.getObject().getKey(), model.getObject().getName(), target);
237                 startAt.toggle(target, true);
238             }
239         }, ActionLink.ActionType.EXECUTE, IdRepoEntitlement.REPORT_EXECUTE);
240 
241         panel.add(new ActionLink<>() {
242 
243             private static final long serialVersionUID = -3722207913631435501L;
244 
245             @Override
246             public void onClick(final AjaxRequestTarget target, final ReportTO ignore) {
247                 final ReportTO reportTO = model.getObject();
248                 try {
249                     restClient.delete(reportTO.getKey());
250 
251                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
252                     target.add(container);
253                 } catch (SyncopeClientException e) {
254                     LOG.error("While deleting {}", reportTO.getKey(), e);
255                     SyncopeConsoleSession.get().onException(e);
256                 }
257                 ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
258             }
259         }, ActionLink.ActionType.DELETE, IdRepoEntitlement.REPORT_DELETE, true);
260 
261         return panel;
262     }
263 
264     @Override
265     protected Collection<ActionType> getBatches() {
266         List<ActionType> batches = new ArrayList<>();
267         batches.add(ActionType.EXECUTE);
268         batches.add(ActionType.DELETE);
269         return batches;
270     }
271 
272     @Override
273     protected ReportDataProvider dataProvider() {
274         return new ReportDataProvider(rows);
275     }
276 
277     @Override
278     protected String paginatorRowsKey() {
279         return IdRepoConstants.PREF_REPORT_PAGINATOR_ROWS;
280     }
281 
282     protected abstract void viewReportExecs(ReportTO reportTO, AjaxRequestTarget target);
283 
284     protected class ReportDataProvider extends DirectoryDataProvider<ReportTO> {
285 
286         private static final long serialVersionUID = 4725679400450513556L;
287 
288         private final SortableDataProviderComparator<ReportTO> comparator;
289 
290         public ReportDataProvider(final int paginatorRows) {
291             super(paginatorRows);
292 
293             setSort(Constants.NAME_FIELD_NAME, SortOrder.ASCENDING);
294             comparator = new SortableDataProviderComparator<>(this);
295         }
296 
297         @Override
298         public Iterator<ReportTO> iterator(final long first, final long count) {
299             List<ReportTO> list = restClient.list();
300             list.sort(comparator);
301             return list.subList((int) first, (int) first + (int) count).iterator();
302         }
303 
304         @Override
305         public long size() {
306             return restClient.list().size();
307         }
308 
309         @Override
310         public IModel<ReportTO> model(final ReportTO report) {
311             return new CompoundPropertyModel<>(report);
312         }
313     }
314 }