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.wicket.markup.html.form;
20  
21  import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
22  import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.FileInputConfig;
23  import java.io.ByteArrayInputStream;
24  import java.util.ArrayList;
25  import java.util.Base64;
26  import java.util.Locale;
27  import java.util.Optional;
28  import javax.ws.rs.core.HttpHeaders;
29  import javax.ws.rs.core.MediaType;
30  import javax.ws.rs.core.Response;
31  import org.apache.commons.lang3.StringUtils;
32  import org.apache.syncope.client.console.SyncopeConsoleSession;
33  import org.apache.syncope.client.console.SyncopeWebApplication;
34  import org.apache.syncope.client.console.commons.PreviewUtils;
35  import org.apache.syncope.client.ui.commons.Constants;
36  import org.apache.syncope.client.ui.commons.HttpResourceStream;
37  import org.apache.syncope.client.ui.commons.markup.html.form.BaseBinaryFieldPanel;
38  import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldDownload;
39  import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
40  import org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
41  import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
42  import org.apache.syncope.client.ui.commons.rest.ResponseHolder;
43  import org.apache.wicket.Component;
44  import org.apache.wicket.ajax.AjaxRequestTarget;
45  import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
46  import org.apache.wicket.ajax.markup.html.AjaxLink;
47  import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
48  import org.apache.wicket.markup.html.WebMarkupContainer;
49  import org.apache.wicket.markup.html.basic.Label;
50  import org.apache.wicket.markup.html.form.Form;
51  import org.apache.wicket.markup.html.form.StatelessForm;
52  import org.apache.wicket.markup.html.form.TextField;
53  import org.apache.wicket.markup.html.form.upload.FileUpload;
54  import org.apache.wicket.markup.html.panel.Fragment;
55  import org.apache.wicket.model.IModel;
56  import org.apache.wicket.model.ResourceModel;
57  import org.apache.wicket.model.util.ListModel;
58  import org.apache.wicket.spring.injection.annot.SpringBean;
59  import org.apache.wicket.util.lang.Bytes;
60  
61  public class BinaryFieldPanel extends BaseBinaryFieldPanel {
62  
63      private static final long serialVersionUID = 6264462604183088931L;
64  
65      @SpringBean
66      protected PreviewUtils previewUtils;
67  
68      protected final String mimeType;
69  
70      protected final WebMarkupContainer container;
71  
72      protected final AjaxLink<Void> downloadLink;
73  
74      protected final Form<?> uploadForm;
75  
76      protected final Fragment emptyFragment;
77  
78      protected final BootstrapFileInputField fileUpload;
79  
80      protected final BinaryFieldDownload fileDownload;
81  
82      protected final BinaryPreviewer previewer;
83  
84      protected final IndicatingAjaxLink<Void> resetLink;
85  
86      protected final Bytes maxUploadSize;
87  
88      protected final IModel<String> model;
89  
90      protected final String fileKey;
91  
92      public BinaryFieldPanel(
93              final String id,
94              final String name,
95              final IModel<String> model,
96              final String mimeType,
97              final String fileKey) {
98  
99          super(id, name, model);
100         this.model = model;
101         this.fileKey = fileKey;
102         this.mimeType = mimeType;
103 
104         previewer = previewUtils.getPreviewer(mimeType);
105 
106         maxUploadSize = Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
107         uploadForm = new StatelessForm<>("uploadForm");
108         uploadForm.setMultiPart(true);
109         add(uploadForm);
110 
111         container = new WebMarkupContainer("previewContainer");
112         container.setOutputMarkupId(true);
113 
114         emptyFragment = new Fragment("panelPreview", "emptyFragment", container);
115         emptyFragment.setOutputMarkupId(true);
116         container.add(emptyFragment);
117         uploadForm.add(container);
118 
119         field = new TextField<>("textField", model);
120         add(field.setLabel(new ResourceModel(name, name)).setOutputMarkupId(true));
121 
122         uploadForm.add(new Label("preview", StringUtils.isBlank(mimeType) ? StringUtils.EMPTY : '(' + mimeType + ')'));
123 
124         fileDownload = new BinaryFieldDownload(name, fileKey, mimeType, true) {
125 
126             private static final long serialVersionUID = 7203445884857810583L;
127 
128             @Override
129             protected HttpResourceStream getResourceStream() {
130                 return new HttpResourceStream(new ResponseHolder(buildResponse()));
131             }
132         };
133 
134         add(fileDownload);
135 
136         downloadLink = new AjaxLink<>("downloadLink") {
137 
138             private static final long serialVersionUID = -4331619903296515985L;
139 
140             @Override
141             public void onClick(final AjaxRequestTarget target) {
142                 try {
143                     fileDownload.initiate(target);
144                 } catch (Exception e) {
145                     SyncopeConsoleSession.get().onException(e);
146                 }
147             }
148         };
149         downloadLink.setOutputMarkupId(true);
150         uploadForm.add(downloadLink);
151 
152         FileInputConfig config = new FileInputConfig().
153                 showUpload(false).showRemove(false).showPreview(false).
154                 browseClass("btn btn-success").browseIcon("<i class=\"fas fa-folder-open\"></i> &nbsp;");
155         String language = SyncopeConsoleSession.get().getLocale().getLanguage();
156         if (!Locale.ENGLISH.getLanguage().equals(language)) {
157             config.withLocale(language);
158         }
159         fileUpload = new BootstrapFileInputField("fileUpload", new ListModel<>(new ArrayList<>()), config);
160         fileUpload.add(new AjaxFormSubmitBehavior(Constants.ON_CHANGE) {
161 
162             private static final long serialVersionUID = -1107858522700306810L;
163 
164             @Override
165             protected void onSubmit(final AjaxRequestTarget target) {
166                 FileUpload uploaded = fileUpload.getFileUpload();
167                 if (uploaded != null) {
168                     if (maxUploadSize != null && uploaded.getSize() > maxUploadSize.bytes()) {
169                         // SYNCOPE-1213 manage directly max upload file size (if set in properties file)
170                         SyncopeConsoleSession.get().error(getString("tooLargeFile").
171                                 replace("${maxUploadSizeB}", String.valueOf(maxUploadSize.bytes())).
172                                 replace("${maxUploadSizeMB}", String.valueOf(maxUploadSize.bytes() / 1000000L)));
173                         ((BaseWebPage) getPageReference().getPage()).getNotificationPanel().refresh(target);
174                     } else {
175                         byte[] uploadedBytes = uploaded.getBytes();
176                         String uploadedEncoded = Base64.getEncoder().encodeToString(uploadedBytes);
177                         field.setModelObject(uploadedEncoded);
178                         target.add(field);
179 
180                         Component panelPreview = previewer.preview(uploadedBytes);
181                         changePreviewer(panelPreview);
182                         fileUpload.setModelObject(null);
183                         uploadForm.addOrReplace(fileUpload);
184 
185                         setVisibleFileButtons(StringUtils.isNotBlank(uploadedEncoded));
186                         downloadLink.setEnabled(StringUtils.isNotBlank(uploadedEncoded));
187 
188                         target.add(uploadForm);
189                     }
190                 }
191             }
192         });
193         uploadForm.add(fileUpload);
194 
195         resetLink = new IndicatingAjaxLink<>("resetLink") {
196 
197             private static final long serialVersionUID = -7978723352517770644L;
198 
199             @Override
200             public void onClick(final AjaxRequestTarget target) {
201                 field.setModelObject(null);
202                 target.add(field);
203                 downloadLink.setEnabled(false);
204                 container.addOrReplace(emptyFragment);
205                 setVisibleFileButtons(false);
206                 target.add(uploadForm);
207             }
208 
209             @Override
210             public String getAjaxIndicatorMarkupId() {
211                 return Constants.VEIL_INDICATOR_MARKUP_ID;
212             }
213 
214         };
215         uploadForm.add(resetLink);
216     }
217 
218     protected Response buildResponse() {
219         return Response.ok(new ByteArrayInputStream(Base64.getMimeDecoder().decode(getModelObject()))).
220                 type(StringUtils.isBlank(mimeType) ? MediaType.APPLICATION_OCTET_STREAM : mimeType).
221                 header(HttpHeaders.LOCATION, StringUtils.EMPTY).
222                 build();
223     }
224 
225     protected void changePreviewer(final Component panelPreview) {
226         Fragment fragment = new Fragment("panelPreview", "previewFragment", container);
227         fragment.add(panelPreview);
228         container.addOrReplace(fragment);
229         uploadForm.addOrReplace(container);
230     }
231 
232     private void setVisibleFileButtons(final boolean visible) {
233         resetLink.setVisible(visible);
234         downloadLink.setVisible(visible);
235     }
236 
237     @Override
238     public BinaryFieldPanel clone() {
239         LOG.debug("Custom clone for binary field panel...");
240         return new BinaryFieldPanel(getId(), this.name, this.model, this.mimeType, this.fileKey);
241     }
242 
243     @Override
244     public FieldPanel<String> setNewModel(final IModel<String> model) {
245         field.setModel(model);
246         String modelObj = model.getObject();
247 
248         if (StringUtils.isNotBlank(modelObj)) {
249             Optional.ofNullable(previewer.preview(modelObj)).ifPresent(this::changePreviewer);
250         }
251 
252         downloadLink.setEnabled(StringUtils.isNotBlank(modelObj));
253         setVisibleFileButtons(StringUtils.isNotBlank(modelObj));
254         return this;
255     }
256 
257     @Override
258     protected void sendError(final Exception exception) {
259         SyncopeConsoleSession.get().onException(exception);
260     }
261 
262     @Override
263     public FieldPanel<String> setReadOnly(final boolean readOnly) {
264         super.setReadOnly(readOnly);
265         fileUpload.setEnabled(!readOnly);
266         return this;
267     }
268 }