Working upload of photos (individual and zip)
[photos] / src / main / java / org / wamblee / photos / wicket / AlbumPanel.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.photos.wicket;
17
18 import java.io.ByteArrayOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.Serializable;
22 import java.util.logging.Logger;
23 import javax.inject.Inject;
24 import javax.servlet.ServletContext;
25
26 import org.apache.wicket.PageParameters;
27 import org.apache.wicket.markup.ComponentTag;
28 import org.apache.wicket.markup.html.WebMarkupContainer;
29 import org.apache.wicket.markup.html.basic.Label;
30 import org.apache.wicket.markup.html.image.Image;
31 import org.apache.wicket.markup.html.link.Link;
32 import org.apache.wicket.markup.html.panel.Panel;
33 import org.apache.wicket.markup.repeater.RepeatingView;
34 import org.wamblee.general.ValueHolder;
35 import org.wamblee.photos.model.Album;
36 import org.wamblee.photos.model.Photo;
37 import org.wamblee.photos.model.PhotoEntry;
38 import org.wamblee.photos.model.plumbing.AuthorizedPhotos;
39
40 /**
41  * Homepage
42  */
43 public class AlbumPanel extends Panel {
44
45     public static class MyValueHolder<T extends Serializable> extends ValueHolder<T> implements Serializable {
46         public MyValueHolder(T aValue) {
47             super(aValue);
48         }
49
50         public MyValueHolder() {
51             super();
52         }
53     }
54
55     private static final Logger LOGGER = Logger.getLogger(AlbumPanel.class.getName());
56
57     private static final long serialVersionUID = 1L;
58     public static final int MAX_ROWS = 5;
59     public static final int MAX_COLUMNS = 5;
60
61     @Inject
62     @AuthorizedPhotos
63     private transient Album authorized;
64
65     @Inject
66     private ServletContext context;
67
68     private class SerializableEntryLink extends Link {
69
70         private String path;
71
72         public SerializableEntryLink(String aId, String aPath) {
73             super(aId);
74             path = aPath;
75         }
76
77         @Override
78         public void onClick() {
79             PageParameters pars = new PageParameters();
80             pars.put("path", path);
81
82             setResponsePage(HomePage.class, pars);
83         }
84     }
85
86     private String path;
87     private int index;
88
89     /**
90      * Constructor that is invoked when page is invoked without a session.
91      *
92      * @param parameters Page parameters
93      */
94     public AlbumPanel(String aId, final PageParameters parameters) throws Exception {
95         super(aId);
96
97         path = parameters.getString("path", "/");
98         if (!path.startsWith("/")) {
99             info("Invalid album '" + path + "', showing root album instead");
100             path = "/";
101         }
102         add(new Label("path", path));
103
104         index = 0;
105         String indexString = parameters.getString("index", "0");
106         try {
107             index = Integer.parseInt(indexString);
108         }
109         catch (NumberFormatException e) {
110             // use default value 0
111         }
112         if (index < 0) {
113             index = 0;
114         }
115
116         Link prevLink = new Link("prevLink") {
117             @Override
118             public void onClick() {
119                 PageParameters pars = new PageParameters();
120                 pars.put("path", path);
121                 pars.put("index", index - MAX_ROWS * MAX_COLUMNS);
122                 setResponsePage(HomePage.class, pars);
123             }
124
125             @Override
126             public boolean isEnabled() {
127                 return index - MAX_ROWS * MAX_COLUMNS >= 0;
128             }
129         };
130         add(prevLink);
131
132         // Avoid implicit references to the album to keep the link objects
133         // small and serializable.
134         final int albumSize = getAlbum().size();
135         Link nextLink = new Link("nextLink") {
136             @Override
137             public void onClick() {
138                 PageParameters pars = new PageParameters();
139                 pars.put("path", path);
140                 pars.put("index", index + MAX_ROWS * MAX_COLUMNS);
141                 setResponsePage(HomePage.class, pars);
142             }
143
144             @Override
145             public boolean isEnabled() {
146                 return index + MAX_ROWS * MAX_COLUMNS < albumSize;
147             }
148         };
149         add(nextLink);
150
151         Link parentLink = new Link("parentLink") {
152             {
153                 if ("/".equals(path)) {
154                     setEnabled(false);
155                 }
156             }
157
158             @Override
159             public void onClick() {
160                 PageParameters pars = new PageParameters();
161                 String parentPath = path.substring(0, path.lastIndexOf("/"));
162                 if (parentPath.length() == 0) {
163                     parentPath = "/";
164                 }
165                 pars.put("path", parentPath);
166                 pars.put("index", 0);
167                 setResponsePage(HomePage.class, pars);
168             }
169         };
170         add(parentLink);
171
172         RepeatingView pageLinks = new RepeatingView("pageLinks");
173         add(pageLinks);
174         Album album = getAlbum();
175         for (int i = 0; i < album.size() / MAX_ROWS / MAX_COLUMNS; i++) {
176             final int istart = i * MAX_ROWS * MAX_COLUMNS;
177             Link pageLink = new Link("pageLink") {
178                 {
179                     if (istart == index) {
180                         setEnabled(false);
181                     }
182                 }
183
184                 @Override
185                 public void onClick() {
186                     PageParameters pars = new PageParameters();
187                     pars.put("path", path);
188                     pars.put("index", istart);
189                     setResponsePage(HomePage.class, pars);
190                 }
191             };
192             pageLink.add(new Label("label", i + ""));
193             pageLinks.add(pageLink);
194             WebMarkupContainer container = new WebMarkupContainer(pageLinks.newChildId());
195             container.add(pageLink);
196             pageLinks.add(container);
197         }
198
199         RepeatingView row = new RepeatingView("row") {
200             @Override
201             protected void onPopulate() {
202                 removeAll();
203                 final ValueHolder<Integer> ientry = new MyValueHolder<Integer>(index);
204                 int irow = 0;
205                 Album album = getAlbum();
206                 while (irow < MAX_ROWS && ientry.getValue() < album.size()) {
207                     WebMarkupContainer columns = new WebMarkupContainer(newChildId());
208                     add(columns);
209                     RepeatingView column = new RepeatingView("column") {
210                         @Override
211                         protected void onPopulate() {
212                             removeAll();
213                             int icolumn = 0;
214                             Album album = getAlbum();
215                             while (icolumn < MAX_COLUMNS && ientry.getValue() < album.size()) {
216                                 WebMarkupContainer thumbnail = new WebMarkupContainer(newChildId());
217                                 add(thumbnail);
218
219                                 final PhotoEntry entry = album.getEntry(ientry.getValue());
220                                 Link link = new SerializableEntryLink("thumbnail", entry.getPath());
221                                 thumbnail.add(link);
222                                 //ImageData data = getData(entry);
223
224                                 // TODO very inefficient. all data is loaded when generating the page.
225                                 //link.add(new Image("image",
226                                 //        new ByteArrayResource(data.getContentType(), data.getData())));
227
228                                 //link.add(new Image("image",
229                                 //        new ContextRelativeResource("image/thumbnail" + entry.getPath())));
230
231                                 //final String url = "/image/thumbnail/" + entry.getPath();
232
233                                 if (entry instanceof Photo) {
234                                     link.add(new Image("image") {
235                                         @Override
236                                         protected void onComponentTag(ComponentTag tag) {
237                                             tag.put("src",
238                                                     context.getContextPath() + "/image/thumbnail/" + entry.getPath());
239                                         }
240                                     });
241                                 } else {
242                                     link.add(new Image("image") {
243                                         @Override
244                                         protected void onComponentTag(ComponentTag tag) {
245                                             //tag.put("src", context.getContextPath() + "/resources/folder.png" +
246                                             //        entry.getPath());
247                                             tag.put("src", context.getContextPath() + "/image/resource/folder.png");
248                                         }
249                                     });
250                                 }
251
252                                 link.add(new Label("name", album.getEntry(ientry.getValue()).getId()));
253                                 icolumn++;
254                                 ientry.setValue(ientry.getValue() + 1);
255                             }
256                         }
257                     };
258                     columns.add(column);
259                     irow++;
260                 }
261             }
262         };
263
264         add(row);
265
266         // upload panel
267         if (path.equals("/"))
268
269         {
270             add(new WebMarkupContainer("uploadPanel"));
271         } else
272
273         {
274             add(new UploadPanel("uploadPanel", path));
275         }
276     }
277
278     private Album getAlbum() {
279         PhotoEntry current = authorized.getEntry(path);
280         if (current instanceof Photo) {
281             throw new RuntimeException("AlbumPanel can only show album: " + current.getClass().getName());
282         }
283         return (Album) current;
284     }
285
286     public static final class ImageData {
287         private String contentType;
288         private byte[] data;
289
290         public ImageData(String aContentType, byte[] aData) {
291             contentType = aContentType;
292             data = aData;
293         }
294
295         public String getContentType() {
296             return contentType;
297         }
298
299         public byte[] getData() {
300             return data;
301         }
302     }
303
304     private ImageData getData(PhotoEntry aEntry) {
305         if (aEntry instanceof Photo) {
306             return getData((Photo) aEntry);
307         } else if (aEntry instanceof Album) {
308             return getData((Album) aEntry);
309         } else {
310             throw new RuntimeException("Unsupported type " + aEntry.getClass().getName());
311         }
312     }
313
314     private ImageData getData(Photo aPhoto) {
315         try (InputStream is = aPhoto.getThumbNail()) {
316             return new ImageData("image/jpeg", getBytes(is));
317         }
318         catch (IOException e) {
319             // to improve.
320             throw new RuntimeException("Cannot read photo", e);
321         }
322     }
323
324     private byte[] getBytes(InputStream is) throws IOException {
325         ByteArrayOutputStream bos = new ByteArrayOutputStream();
326         byte[] block = new byte[1024];
327         int n = is.read(block);
328         while (n > 0) {
329             bos.write(block, 0, n);
330             n = is.read(block);
331         }
332         return bos.toByteArray();
333     }
334
335     private ImageData getData(Album aAlbum) {
336         try (InputStream is = getClass().getResourceAsStream("folder.png")) {
337             return new ImageData("image/png", getBytes(is));
338         }
339         catch (IOException e) {
340             throw new RuntimeException("Cannot read album jpg", e);
341         }
342     }
343 }