1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.form.field.upload.basic;
35
36 import info.magnolia.i18nsystem.SimpleTranslator;
37 import info.magnolia.icons.MagnoliaIcons;
38 import info.magnolia.ui.api.context.UiContext;
39 import info.magnolia.ui.form.field.definition.BasicUploadFieldDefinition;
40 import info.magnolia.ui.form.field.upload.AbstractUploadField;
41 import info.magnolia.ui.form.field.upload.UploadProgressIndicator;
42 import info.magnolia.ui.form.field.upload.UploadReceiver;
43 import info.magnolia.ui.imageprovider.ImageProvider;
44 import info.magnolia.ui.theme.ResurfaceTheme;
45 import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
46
47 import org.apache.commons.io.FileUtils;
48 import org.apache.commons.lang3.StringUtils;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import com.vaadin.data.HasValue;
53 import com.vaadin.server.Resource;
54 import com.vaadin.shared.ui.ContentMode;
55 import com.vaadin.ui.Button;
56 import com.vaadin.ui.Component;
57 import com.vaadin.ui.CssLayout;
58 import com.vaadin.ui.FormLayout;
59 import com.vaadin.ui.Label;
60 import com.vaadin.ui.Layout;
61 import com.vaadin.ui.TextField;
62
63
64
65
66
67
68
69
70
71
72
73
74 public class ResurfaceBasicUploadField<T extends UploadReceiver> extends AbstractUploadField<T> {
75 private static final long serialVersionUID = 1L;
76 private static final Logger log = LoggerFactory.getLogger(ResurfaceBasicUploadField.class);
77
78
79 private final CssLayout layout;
80 private final CssLayout uploadZone;
81 private UploadProgressIndicator progress;
82 protected final ImageProvider imageProvider;
83 private boolean editFileName = false;
84 private boolean editFileFormat = false;
85 protected UiContext uiContext;
86
87 private final SimpleTranslator i18n;
88 private FormLayout fileInfo;
89
90 public ResurfaceBasicUploadField(ImageProvider imageProvider, UiContext uiContext, BasicUploadFieldDefinition definition, SimpleTranslator i18n) {
91 super();
92
93 populateFromDefinition(definition);
94
95 this.imageProvider = imageProvider;
96 this.uiContext = uiContext;
97 this.i18n = i18n;
98
99 layout = new CssLayout();
100 layout.addStyleName("upload-layout");
101
102 uploadZone = new CssLayout();
103 uploadZone.setSizeFull();
104 layout.addComponent(createDropZone(uploadZone));
105
106 setRootLayout(layout);
107 }
108
109
110
111
112
113
114
115 @Override
116 public void attach() {
117 super.attach();
118 updateDisplay();
119 log.debug("Component was attached ...");
120 }
121
122
123
124
125
126
127
128 @Override
129 protected void buildEmptyLayout() {
130 uploadZone.removeAllComponents();
131 if (fileInfo != null) {
132 layout.removeComponent(fileInfo);
133 }
134
135 if (isReadOnly()) {
136 return;
137 }
138
139
140 Label uploadText = new Label(getCaption(dropZoneCaption, null), ContentMode.HTML);
141 uploadText.addStyleName("upload-text");
142 uploadZone.addComponent(uploadText);
143
144
145 getUpload().setButtonCaption(getCaption(selectNewCaption, null));
146 getUpload().addStyleName("upload-button");
147 uploadZone.addComponent(getUpload());
148
149
150 uploadZone.removeStyleName("done");
151 uploadZone.removeStyleName("in-progress");
152 uploadZone.addStyleName("initial");
153
154 log.debug("buildEmptyLayout() called ...");
155 }
156
157
158
159
160
161
162
163 @Override
164 protected void buildInProgressLayout(String uploadedFileMimeType) {
165 uploadZone.removeAllComponents();
166 if (fileInfo != null) {
167 layout.removeComponent(fileInfo);
168 }
169
170
171 progress = new ResurfaceBasicUploadProgressIndicator(inProgressCaption, inProgressRatioCaption, i18n, (Button.ClickListener) event -> interruptUpload(InterruptionReason.USER));
172 progress.setVisible(true);
173 progress.setProgress(0);
174 uploadZone.addComponent(progress.asVaadinComponent());
175
176
177 uploadZone.removeStyleName("done");
178 uploadZone.removeStyleName("initial");
179 uploadZone.addStyleName("in-progress");
180
181 log.debug("buildInProgressLayout() called ...");
182 }
183
184 @Override
185 protected void refreshInProgressLayout(long readBytes, long contentLength, String fileName) {
186 if (progress != null) {
187 progress.refreshLayout(readBytes, contentLength, fileName);
188 }
189 }
190
191
192
193
194
195
196
197
198
199
200
201 @Override
202 protected void buildCompletedLayout() {
203 if (fileInfo != null) {
204 layout.removeComponent(fileInfo);
205 }
206 createCompletedActionLayout();
207
208 layout.addComponent(createFileInfoComponent());
209
210 log.debug("buildCompletedLayout() called ...");
211 }
212
213
214
215
216 protected Layout createCompletedActionLayout() {
217 uploadZone.removeAllComponents();
218
219
220 uploadZone.addStyleName("finished-action-layout");
221 addControlActions(uploadZone);
222
223
224 uploadZone.addComponent(createThumbnailComponent());
225
226
227 getUpload().setButtonCaption(getCaption(selectAnotherCaption, null));
228 getUpload().addStyleName("upload-another");
229 uploadZone.addComponent(getUpload());
230
231
232 uploadZone.removeStyleName("in-progress");
233 uploadZone.removeStyleName("initial");
234 uploadZone.addStyleName("done");
235
236 return uploadZone;
237 }
238
239 protected void addControlActions(final CssLayout uploadZone) {
240
241 if (!getValue().isEmpty()) {
242 uploadZone.addComponent(createDeleteButton());
243 }
244 }
245
246
247
248
249 protected Button createDeleteButton() {
250 Button deleteButton = createControlButton(deleteCaption, MagnoliaIcons.TRASH);
251
252 deleteButton.addClickListener((Button.ClickListener) event -> {
253 resetDataSource();
254 updateDisplay();
255 });
256 return deleteButton;
257 }
258
259 protected Button createControlButton(String captionkey, Resource icon) {
260 Button controlButton = new Button(icon);
261 controlButton.addStyleNames(ResurfaceTheme.BUTTON_ICON, "edit-action");
262 controlButton.setDescription(i18n.translate(captionkey));
263 return controlButton;
264 }
265
266
267
268
269
270
271 protected Component getFileName() {
272
273 final boolean hasExtension = StringUtils.isNotBlank(getValue().getExtension());
274 final String extension = hasExtension ? "." + getValue().getExtension() : "";
275 String fileName = StringUtils.removeEnd(getValue().getFileName(), extension);
276
277 if (this.editFileName && !isReadOnly()) {
278 TextField textField = new TextField(i18n.translate(fileDetailNameCaption), fileName);
279 textField.setCaption(i18n.translate(fileDetailNameCaption));
280 textField.addValueChangeListener((HasValue.ValueChangeListener<String>) event -> {
281 Object newFileNameObject = event.getValue();
282 String newFileName = (newFileNameObject != null && StringUtils.isNotBlank(newFileNameObject.toString())) ? newFileNameObject.toString() : UploadReceiver.INVALID_FILE_NAME;
283 getValue().setFileName(newFileName + extension);
284 getPropertyDataSource().setValue(getValue());
285 });
286 return textField;
287 } else {
288 Label label = new Label();
289 label.setCaption(i18n.translate(fileDetailNameCaption));
290 label.setValue(fileName);
291 return label;
292 }
293 }
294
295
296
297
298
299
300
301 private Component createFileInfoComponent() {
302 if (fileInfo == null) {
303 fileInfo = new FormLayout();
304 }
305 fileInfo.removeAllComponents();
306 fileInfo.setSizeUndefined();
307 fileInfo.addStyleName("file-details");
308 fileInfo.addComponent(getFileName());
309 fileInfo.addComponent(getFileDetailSize());
310 fileInfo.addComponent(getFileDetailFileFormat());
311 return fileInfo;
312 }
313
314
315
316
317 protected Component getFileDetailSize() {
318 Label label = new Label();
319 label.setCaption(i18n.translate(fileDetailSizeCaption));
320 label.setValue(FileUtils.byteCountToDisplaySize(getValue().getFileSize()));
321 return label;
322 }
323
324
325
326
327
328
329 protected Component getFileDetailFileFormat() {
330 if (this.editFileFormat && !isReadOnly()) {
331 TextField textField = new TextField(i18n.translate(fileDetailFormatCaption), getValue().getExtension());
332 textField.setCaption(i18n.translate(fileDetailFormatCaption));
333 return textField;
334 } else {
335 Label label = new Label();
336 label.setValue(getValue().getExtension());
337 label.setCaption(i18n.translate(fileDetailFormatCaption));
338 return label;
339 }
340 }
341
342
343
344
345 protected Component createThumbnailComponent() {
346 Label thumbnail = new Label();
347 thumbnail.setSizeUndefined();
348 thumbnail.addStyleName("file-preview");
349 thumbnail.addStyleName(createIconStyleName());
350 return thumbnail;
351 }
352
353
354
355
356
357
358
359 protected String createIconStyleName() {
360 return "icon-" + imageProvider.resolveIconClassName(getValue().getMimeType());
361 }
362
363 @Override
364 protected Component initContent() {
365 return getRootLayout();
366 }
367
368 protected SimpleTranslator getI18n() {
369 return i18n;
370 }
371
372 protected CssLayout getUploadZone() {
373 return uploadZone;
374 }
375
376
377
378
379 protected void populateFromDefinition(BasicUploadFieldDefinition definition) {
380 this.setMaxUploadSize(definition.getMaxUploadSize());
381 this.setAllowedMimeTypePattern(definition.getAllowedMimeTypePattern());
382
383 this.setSelectNewCaption("field.upload.basic.m6.select.new");
384 this.setSelectAnotherCaption("field.upload.basic.m6.select.another");
385 this.setDropZoneCaption("field.upload.basic.m6.drop.hint");
386 this.setInProgressCaption("field.upload.basic.m6.uploading.file");
387
388 this.setInProgressRatioCaption(definition.getInProgressRatioCaption());
389 this.setFileDetailHeaderCaption(definition.getFileDetailHeaderCaption());
390 this.setFileDetailNameCaption(definition.getFileDetailNameCaption());
391 this.setFileDetailSizeCaption(definition.getFileDetailSizeCaption());
392 this.setFileDetailFormatCaption(definition.getFileDetailFormatCaption());
393 this.setFileDetailSourceCaption(definition.getFileDetailSourceCaption());
394 this.setSuccessNoteCaption(definition.getSuccessNoteCaption());
395 this.setWarningNoteCaption(definition.getWarningNoteCaption());
396 this.setErrorNoteCaption(definition.getErrorNoteCaption());
397 this.setDeteteCaption(definition.getDeleteCaption());
398 this.setEditFileFormat(definition.isEditFileFormat());
399 this.setEditFileName(definition.isEditFileName());
400 this.setUserInterruption(definition.getUserInterruption());
401 this.setTypeInterruption(definition.getTypeInterruption());
402 this.setSizeInterruption(definition.getSizeInterruption());
403 }
404
405 protected String getCaption(String caption, String[] args) {
406 if (StringUtils.isEmpty(caption)) {
407 return "";
408 }
409 if (args != null && args.length > 0) {
410 return i18n.translate(caption, args);
411 } else {
412 return i18n.translate(caption);
413 }
414 }
415
416 protected String selectNewCaption;
417 protected String selectAnotherCaption;
418 protected String deleteCaption;
419 protected String dropZoneCaption;
420 protected String inProgressCaption;
421 protected String inProgressRatioCaption;
422 protected String fileDetailHeaderCaption;
423 protected String fileDetailNameCaption;
424 protected String fileDetailSizeCaption;
425 protected String fileDetailFormatCaption;
426 protected String fileDetailSourceCaption;
427 protected String successNoteCaption;
428 protected String warningNoteCaption;
429 protected String errorNoteCaption;
430 private String sizeInterruption;
431 private String typeInterruption;
432 private String userInterruption;
433
434 public void setSelectNewCaption(String selectNewCaption) {
435 this.selectNewCaption = selectNewCaption;
436 }
437
438 public void setSelectAnotherCaption(String selectAnotherCaption) {
439 this.selectAnotherCaption = selectAnotherCaption;
440 }
441
442 public void setDropZoneCaption(String dropZoneCaption) {
443 this.dropZoneCaption = dropZoneCaption;
444 }
445
446 public void setInProgressCaption(String inProgressCaption) {
447 this.inProgressCaption = inProgressCaption;
448 }
449
450 public void setInProgressRatioCaption(String inProgressRatioCaption) {
451 this.inProgressRatioCaption = inProgressRatioCaption;
452 }
453
454 public void setFileDetailHeaderCaption(String fileDetailHeaderCaption) {
455 this.fileDetailHeaderCaption = fileDetailHeaderCaption;
456 }
457
458 public void setFileDetailNameCaption(String fileDetailNameCaption) {
459 this.fileDetailNameCaption = fileDetailNameCaption;
460 }
461
462 public void setFileDetailSizeCaption(String fileDetailSizeCaption) {
463 this.fileDetailSizeCaption = fileDetailSizeCaption;
464 }
465
466 public void setFileDetailFormatCaption(String fileDetailFormatCaption) {
467 this.fileDetailFormatCaption = fileDetailFormatCaption;
468 }
469
470 public void setFileDetailSourceCaption(String fileDetailSourceCaption) {
471 this.fileDetailSourceCaption = fileDetailSourceCaption;
472 }
473
474 public void setSuccessNoteCaption(String successNoteCaption) {
475 this.successNoteCaption = successNoteCaption;
476 }
477
478 public void setWarningNoteCaption(String warningNoteCaption) {
479 this.warningNoteCaption = warningNoteCaption;
480 }
481
482 public void setErrorNoteCaption(String errorNoteCaption) {
483 this.errorNoteCaption = errorNoteCaption;
484 }
485
486 public void setDeteteCaption(String deleteCaption) {
487 this.deleteCaption = deleteCaption;
488 }
489
490 public void setSizeInterruption(String sizeInterruption) {
491 this.sizeInterruption = sizeInterruption;
492 }
493
494 public void setTypeInterruption(String typeInterruption) {
495 this.typeInterruption = typeInterruption;
496 }
497
498 public void setUserInterruption(String userInterruption) {
499 this.userInterruption = userInterruption;
500 }
501
502 @Override
503 protected void displayUploadInterruptNote(InterruptionReason reason) {
504 String caption;
505 if (reason.equals(InterruptionReason.USER)) {
506 caption = userInterruption;
507 } else if (reason.equals(InterruptionReason.FILE_SIZE)) {
508 caption = sizeInterruption;
509 } else {
510 caption = typeInterruption;
511 }
512 uiContext.openNotification(MessageStyleTypeEnum.WARNING, true, getCaption(warningNoteCaption, new String[] { i18n.translate(caption) }));
513 }
514
515 @Override
516 protected void displayUploadFinishedNote(String fileName) {
517 uiContext.openNotification(MessageStyleTypeEnum.INFO, true, getCaption(successNoteCaption, new String[] { fileName }));
518 }
519
520 @Override
521 protected void displayUploadFailedNote(String fileName) {
522 uiContext.openAlert(MessageStyleTypeEnum.ERROR, "ERROR", getCaption(errorNoteCaption, new String[] { fileName }), i18n.translate("button.ok"), null);
523 }
524
525 public void setEditFileName(boolean editFileName) {
526 this.editFileName = editFileName;
527 }
528
529 public void setEditFileFormat(boolean editFileFormat) {
530 this.editFileFormat = editFileFormat;
531 }
532
533 @Override
534 public boolean isEmpty() {
535 return getValue().isEmpty();
536 }
537
538 @Override
539 public void setReadOnly(boolean readOnly) {
540 super.setReadOnly(readOnly);
541 if (readOnly) {
542
543 if (getDropZone() != null) {
544 getDropZone().setDropHandler(null);
545 }
546 if (getUpload() != null) {
547 getUpload().removeStartedListener(this);
548 getUpload().removeFinishedListener(this);
549 getUpload().removeProgressListener(this);
550 }
551 if (getValue().isEmpty()) {
552 buildEmptyLayout();
553 }
554 }
555
556 }
557 }