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.incub;
35
36 import static org.apache.commons.lang3.StringUtils.EMPTY;
37
38 import info.magnolia.icons.MagnoliaIcons;
39 import info.magnolia.ui.theme.ResurfaceTheme;
40
41 import java.io.File;
42 import java.io.FileOutputStream;
43 import java.io.IOException;
44 import java.io.OutputStream;
45 import java.util.Arrays;
46 import java.util.Collection;
47
48 import org.apache.commons.io.FileUtils;
49 import org.apache.commons.lang3.StringUtils;
50
51 import com.google.common.net.MediaType;
52 import com.vaadin.server.FileResource;
53 import com.vaadin.server.Resource;
54 import com.vaadin.server.StreamVariable;
55 import com.vaadin.ui.Button;
56 import com.vaadin.ui.Component;
57 import com.vaadin.ui.CssLayout;
58 import com.vaadin.ui.CustomField;
59 import com.vaadin.ui.HorizontalLayout;
60 import com.vaadin.ui.Html5File;
61 import com.vaadin.ui.Image;
62 import com.vaadin.ui.Label;
63 import com.vaadin.ui.Notification;
64 import com.vaadin.ui.Upload;
65 import com.vaadin.ui.VerticalLayout;
66 import com.vaadin.ui.dnd.FileDropTarget;
67
68
69
70
71
72
73 public class UploadField extends CustomField {
74
75 private static final String UPLOAD_CAPTION = "Upload";
76 private static final String UPLOAD_ANOTHER_CAPTION = "Upload another";
77 private static final Resource DEFAULT_REVIEW_IMG = MagnoliaIcons.FILE;
78 private static final Collection<String> IMG_MIME_TYPES = Arrays.asList(MediaType.JPEG.toString(), MediaType.PNG.toString(), MediaType.BMP.toString());
79 private final VerticalLayout rootLayout;
80 private final UploadFieldDetailComponent detailComponent;
81
82 private String currentFileName = EMPTY;
83 private String currentFilePath = EMPTY;
84 private String currentFileSize = EMPTY;
85 private String currentFileType = EMPTY;
86
87 private Upload uploadBtn;
88
89 private Button removeUploadBtn;
90
91 private Label dragLabel;
92 private Image thumbnail;
93 private CssLayout uploadPanel;
94
95 public UploadField() {
96 super();
97
98 this.rootLayout = new VerticalLayout();
99 this.rootLayout.setSizeFull();
100 this.rootLayout.setMargin(false);
101 this.rootLayout.setSpacing(true);
102
103 detailComponent = new UploadFieldDetailComponent();
104
105 }
106
107 @Override
108 public Object getValue() {
109 return null;
110 }
111
112 @Override
113 protected void finalize() throws Throwable {
114 super.finalize();
115 }
116
117 @Override
118 protected Component initContent() {
119
120 HorizontalLayout content = new HorizontalLayout();
121 content.setSizeFull();
122 content.setSpacing(false);
123
124 uploadPanel = new CssLayout();
125 uploadPanel.setSizeFull();
126 uploadPanel.setStyleName("upload-file-panel");
127 uploadPanel.setHeightUndefined();
128
129 CssLayout controlButtonPanel = new CssLayout();
130 controlButtonPanel.addStyleName("control-button-panel");
131 controlButtonPanel.setWidth(30, Unit.PIXELS);
132
133 removeUploadBtn = createControlPanelButton(MagnoliaIcons.DELETE);
134 removeUploadBtn.addClickListener(event -> {
135 FileUtils.deleteQuietly(new File(currentFilePath));
136 currentFileName = EMPTY;
137 currentFileSize = EMPTY;
138 currentFileType = EMPTY;
139 updateControlVisibilities();
140 });
141 controlButtonPanel.addComponent(removeUploadBtn);
142 additionalUploadButtons(controlButtonPanel);
143
144 thumbnail = new Image("", DEFAULT_REVIEW_IMG);
145 thumbnail.addStyleName("file-preview-thumbnail");
146
147 this.dragLabel = createDragFileUploadLabel();
148
149 Upload.Receiver receiver = (Upload.Receiver) (filename, mimeType) -> getUploadFileOutputStream();
150 uploadBtn = new Upload("", receiver);
151 uploadBtn.addSucceededListener((Upload.SucceededListener) event -> {
152 currentFileName = event.getFilename();
153 currentFileSize = String.format("%s KB", event.getLength() / 1024);
154 currentFileType = event.getMIMEType();
155 updateControlVisibilities();
156 });
157 uploadBtn.addStyleName("upload-button");
158
159 uploadPanel.addComponents(controlButtonPanel, dragLabel, thumbnail, uploadBtn);
160
161 content.addComponents(uploadPanel);
162 content.setExpandRatio(uploadPanel, 1);
163
164 updateControlVisibilities();
165
166 rootLayout.addComponents(content);
167
168 return rootLayout;
169 }
170
171 protected void additionalUploadButtons(CssLayout controlButtonPanel) {
172 }
173
174 @Override
175 protected void doSetValue(Object value) {
176 }
177
178 protected Button createControlPanelButton(Resource icon) {
179 Button button = new Button(icon);
180 button.addStyleNames(ResurfaceTheme.BUTTON_ICON, "control-button");
181 return button;
182 }
183
184 protected void updateControlVisibilities() {
185
186 removeUploadBtn.setVisible(hasFileUploaded());
187
188 updatePreviewThumbnail();
189 dragLabel.setVisible(!hasFileUploaded());
190 thumbnail.setVisible(hasFileUploaded());
191
192 if (hasFileUploaded()) {
193 detailComponent.updateFileDetail(currentFileName, currentFileSize, currentFileType);
194 rootLayout.addComponent(detailComponent);
195 } else {
196 rootLayout.removeComponent(detailComponent);
197 }
198
199 uploadBtn.setButtonCaption(StringUtils.isEmpty(currentFileName) ? UPLOAD_CAPTION : UPLOAD_ANOTHER_CAPTION);
200
201 }
202
203 protected boolean hasFileUploaded() {
204 return StringUtils.isNotEmpty(currentFileName);
205 }
206
207 private Label createDragFileUploadLabel() {
208 Label dragLabel = new Label("Drag a file into this area or select a file");
209 dragLabel.setStyleName("drag-to-upload-label");
210 new FileDropTarget<>(dragLabel, event -> {
211
212 Collection<Html5File> files = event.getFiles();
213 files.forEach(file -> {
214 file.setStreamVariable(new StreamVariable() {
215
216 @Override
217 public OutputStream getOutputStream() {
218 return getUploadFileOutputStream();
219 }
220
221 @Override
222 public boolean listenProgress() {
223 return true;
224 }
225
226 @Override
227 public boolean isInterrupted() {
228 return false;
229 }
230
231 @Override
232 public void onProgress(StreamingProgressEvent event) {
233 Notification.show("Progress, bytesReceived="
234 + event.getBytesReceived());
235 }
236
237 @Override
238 public void streamingStarted(StreamingStartEvent event) {
239 Notification.show("Stream started, fileName="
240 + event.getFileName());
241 }
242
243 @Override
244 public void streamingFailed(StreamingErrorEvent event) {
245 Notification.show("Stream failed, fileName="
246 + event.getFileName(), Notification.Type.ERROR_MESSAGE);
247 }
248
249 @Override
250 public void streamingFinished(StreamingEndEvent event) {
251 currentFileName = event.getFileName();
252 currentFileSize = String.format("%s MB", event.getBytesReceived() / 1024 / 1024);
253 currentFileType = event.getMimeType();
254 updateControlVisibilities();
255 Notification.show("Stream finished, fileName="
256 + event.getFileName());
257 }
258 });
259 });
260 });
261 return dragLabel;
262 }
263
264 private OutputStream getUploadFileOutputStream() {
265 try {
266 final File tempFile = createTempFile();
267 final FileOutputStream fos = FileUtils.openOutputStream(tempFile);
268 currentFilePath = tempFile.getPath();
269 return fos;
270 } catch (IOException ignore) {
271 return null;
272 }
273 }
274
275 private File createTempFile() throws IOException {
276 final File tempFile = File.createTempFile("upload", "*.tmp");
277 tempFile.deleteOnExit();
278 return tempFile;
279 }
280
281 private void updatePreviewThumbnail() {
282 if (hasFileUploaded()) {
283 if (IMG_MIME_TYPES.contains(currentFileType)) {
284 thumbnail.setIcon(null);
285 thumbnail.setSource(new FileResource(new File(currentFilePath)));
286 uploadPanel.addStyleName("upload-file-panel-large");
287 } else {
288 thumbnail.setIcon(MagnoliaIcons.FILE);
289 thumbnail.setSource(null);
290 uploadPanel.removeStyleName("upload-file-panel-large");
291 }
292 } else {
293 thumbnail.setIcon(null);
294 thumbnail.setSource(null);
295 uploadPanel.removeStyleName("upload-file-panel-large");
296 }
297 }
298 }