When last_modified is not in valid format the date will be set to null
[pithos-web-client] / src / gr / grnet / pithos / web / client / FileList.java
1 /*
2  * Copyright 2011 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.pithos.web.client;
37
38 import gr.grnet.pithos.web.client.foldertree.File;
39 import gr.grnet.pithos.web.client.foldertree.Folder;
40 import gr.grnet.pithos.web.client.foldertree.FolderTreeView;
41
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.Comparator;
45 import java.util.Iterator;
46 import java.util.List;
47
48 import com.google.gwt.cell.client.ImageResourceCell;
49 import com.google.gwt.cell.client.SafeHtmlCell;
50 import com.google.gwt.cell.client.TextCell;
51 import com.google.gwt.cell.client.ValueUpdater;
52 import com.google.gwt.core.client.GWT;
53 import com.google.gwt.event.dom.client.ContextMenuEvent;
54 import com.google.gwt.event.dom.client.ContextMenuHandler;
55 import com.google.gwt.i18n.client.DateTimeFormat;
56 import com.google.gwt.resources.client.ImageResource;
57 import com.google.gwt.safehtml.client.SafeHtmlTemplates;
58 import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
59 import com.google.gwt.safehtml.shared.SafeHtml;
60 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
61 import com.google.gwt.user.cellview.client.CellTable;
62 import com.google.gwt.user.cellview.client.Column;
63 import com.google.gwt.user.client.Event;
64 import com.google.gwt.user.client.Window;
65 import com.google.gwt.user.client.ui.Composite;
66 import com.google.gwt.user.client.ui.VerticalPanel;
67 import com.google.gwt.view.client.ListDataProvider;
68 import com.google.gwt.view.client.MultiSelectionModel;
69 import com.google.gwt.view.client.ProvidesKey;
70
71 /**
72  * A composite that displays the list of files in a particular folder.
73  */
74 public class FileList extends Composite {
75
76         ListDataProvider<File> provider = new ListDataProvider<File>();
77
78     /**
79        * The styles applied to the table.
80        */
81     interface TableStyle extends CellTable.Style {
82     }
83
84         interface TableResources extends CellTable.Resources {
85             @Override
86                 @Source({CellTable.Style.DEFAULT_CSS, "GssCellTable.css"})
87             TableStyle cellTableStyle();
88         }
89         
90         static interface Templates extends SafeHtmlTemplates {
91             Templates INSTANCE = GWT.create(Templates.class);
92
93             @Template("<div id='dragHelper' style='border:1px solid black; background-color:#ffffff; color:black; width:150px;z-index:100'></div>")
94             SafeHtml outerHelper();
95
96         @Template("<span id='{0}'>{0}</span>")
97         public SafeHtml filenameSpan(String filename);
98
99         @Template("<a href='{0}' title='{1}' rel='lytebox[mnf]' onclick='myLytebox.start(this, false, false); return false;'>(view)</a>")
100         public SafeHtml viewLink(String link, String title);
101
102         @Template("<table><tr><td rowspan='3'>{0}</td><td style='font-size:95%;' id='{1}'>{1}</td></tr><tr><td>{2}</td></tr></table>")
103         public SafeHtml rendelContactCell(String imageHtml, String name, String fileSize);
104
105         @Template("<span id='{0}' class='{1}'>{2}</span>")
106         public SafeHtml spanWithIdAndClass(String id, String cssClass, String content);
107         }
108
109         protected final DateTimeFormat formatter = DateTimeFormat.getFormat("d/M/yyyy h:mm a");
110
111         /**
112          * Specifies that the images available for this composite will be the ones
113          * available in FileContextMenu.
114          */
115         public interface Images extends FolderTreeView.Images {
116
117                 @Source("gr/grnet/pithos/resources/blank.gif")
118                 ImageResource blank();
119
120                 @Source("gr/grnet/pithos/resources/asc.png")
121                 ImageResource asc();
122
123                 @Source("gr/grnet/pithos/resources/desc.png")
124                 ImageResource desc();
125
126                 @Source("gr/grnet/pithos/resources/mimetypes/document_shared.png")
127                 ImageResource documentShared();
128
129                 @Source("gr/grnet/pithos/resources/mimetypes/kcmfontinst.png")
130                 ImageResource wordprocessor();
131
132                 @Source("gr/grnet/pithos/resources/mimetypes/log.png")
133                 ImageResource spreadsheet();
134
135                 @Source("gr/grnet/pithos/resources/mimetypes/kpresenter_kpr.png")
136                 ImageResource presentation();
137
138                 @Source("gr/grnet/pithos/resources/mimetypes/acroread.png")
139                 ImageResource pdf();
140
141                 @Source("gr/grnet/pithos/resources/mimetypes/image.png")
142                 ImageResource image();
143
144                 @Source("gr/grnet/pithos/resources/mimetypes/video2.png")
145                 ImageResource video();
146
147                 @Source("gr/grnet/pithos/resources/mimetypes/knotify.png")
148                 ImageResource audio();
149
150                 @Source("gr/grnet/pithos/resources/mimetypes/html.png")
151                 ImageResource html();
152
153                 @Source("gr/grnet/pithos/resources/mimetypes/txt.png")
154                 ImageResource txt();
155
156                 @Source("gr/grnet/pithos/resources/mimetypes/ark2.png")
157                 ImageResource zip();
158
159                 @Source("gr/grnet/pithos/resources/mimetypes/kcmfontinst_shared.png")
160                 ImageResource wordprocessorShared();
161
162                 @Source("gr/grnet/pithos/resources/mimetypes/log_shared.png")
163                 ImageResource spreadsheetShared();
164
165                 @Source("gr/grnet/pithos/resources/mimetypes/kpresenter_kpr_shared.png")
166                 ImageResource presentationShared();
167
168                 @Source("gr/grnet/pithos/resources/mimetypes/acroread_shared.png")
169                 ImageResource pdfShared();
170
171                 @Source("gr/grnet/pithos/resources/mimetypes/image_shared.png")
172                 ImageResource imageShared();
173
174                 @Source("gr/grnet/pithos/resources/mimetypes/video2_shared.png")
175                 ImageResource videoShared();
176
177                 @Source("gr/grnet/pithos/resources/mimetypes/knotify_shared.png")
178                 ImageResource audioShared();
179
180                 @Source("gr/grnet/pithos/resources/mimetypes/html_shared.png")
181                 ImageResource htmlShared();
182
183                 @Source("gr/grnet/pithos/resources/mimetypes/txt_shared.png")
184                 ImageResource txtShared();
185
186                 @Source("gr/grnet/pithos/resources/mimetypes/ark2_shared.png")
187                 ImageResource zipShared();
188
189         }
190         
191         /**
192          * The number of files in this folder.
193          */
194         int folderFileCount;
195
196         /**
197          * Total folder size
198          */
199         long folderTotalSize;
200
201         /**
202          * A cache of the files in the list.
203          */
204         private List<File> files;
205
206         /**
207          * The widget's image bundle.
208          */
209         protected final Images images;
210         
211         protected CellTable<File> celltable;
212
213         private final MultiSelectionModel<File> selectionModel;
214
215         protected final List<SortableHeader> allHeaders = new ArrayList<SortableHeader>();
216
217         SortableHeader nameHeader;
218
219     FolderTreeView treeView;
220
221     protected Pithos app;
222
223     /**
224          * Construct the file list widget. This entails setting up the widget
225          * layout, fetching the number of files in the current folder from the
226          * server and filling the local file cache of displayed files with data from
227          * the server, as well.
228          *
229          * @param _images
230          */
231         public FileList(final Pithos _app, Images _images, FolderTreeView _treeView) {
232         app = _app;
233                 images = _images;
234         this.treeView = _treeView;
235
236         CellTable.Resources resources = GWT.create(TableResources.class);
237
238         ProvidesKey<File> keyProvider = new ProvidesKey<File>(){
239
240                         @Override
241                         public Object getKey(File item) {
242                                 return item.getUri();
243                         }
244                 };
245
246                 celltable = new CellTable<File>(10, resources, keyProvider);
247         celltable.setWidth("100%");
248         celltable.setStyleName("pithos-List");
249
250                 Column<File, ImageResource> status = new Column<File, ImageResource>(new ImageResourceCell() {
251                     @Override
252                 public boolean handlesSelection() {
253                     return false;
254                 }
255                 })
256         {
257                  @Override
258                  public ImageResource getValue(File entity) {
259                      return getFileIcon(entity);
260                  }
261             };
262             celltable.addColumn(status,"");
263
264         final Column<File,SafeHtml> nameColumn = new Column<File,SafeHtml>(new SafeHtmlCell()) {
265
266                         @Override
267                         public SafeHtml getValue(File object) {
268                                 SafeHtmlBuilder sb = new SafeHtmlBuilder();
269                 sb.append(Templates.INSTANCE.filenameSpan(object.getName()));
270                                 if (object.getContentType().endsWith("png") || object.getContentType().endsWith("gif") || object.getContentType().endsWith("jpeg")) {
271                                 sb.appendHtmlConstant("&nbsp;")
272                       .append(Templates.INSTANCE.viewLink(app.getApiPath() + object.getOwner() + object.getUri() + "?X-Auth-Token=" + app.getToken(), object.getName()));
273                                 }
274                                 
275                                 return sb.toSafeHtml();
276                         }
277                         
278                 };
279         celltable.addColumn(nameColumn, nameHeader = new SortableHeader("Name"));
280                 allHeaders.add(nameHeader);
281                 nameHeader.setUpdater(new FileValueUpdater(nameHeader, "name"));
282
283                 celltable.redrawHeaders();
284                 
285         Column<File,String> aColumn = new Column<File,String>(new TextCell()) {
286                         @Override
287                         public String getValue(File object) {
288                                 // TODO Auto-generated method stub
289                                 return object.getSizeAsString();
290                         }
291                 };
292         SortableHeader aheader = new SortableHeader("Size");
293         celltable.addColumn(aColumn, aheader);
294                 allHeaders.add(aheader);
295                 aheader.setUpdater(new FileValueUpdater(aheader, "size"));
296
297         aColumn = new Column<File,String>(new TextCell()) {
298                         @Override
299                         public String getValue(File object) {
300                                 return object.getLastModified() != null ? formatter.format(object.getLastModified()) : "";
301                         }
302                 };
303         aheader = new SortableHeader("Last Modified");
304                 celltable.addColumn(aColumn, aheader);
305                 allHeaders.add(aheader);
306                 aheader.setUpdater(new FileValueUpdater(aheader, "date"));
307                
308                 provider.addDataDisplay(celltable);
309
310                 VerticalPanel vp = new VerticalPanel();
311                 vp.setWidth("100%");
312
313         vp.add(celltable);
314
315                 vp.setCellWidth(celltable, "100%");
316         vp.addHandler(new ContextMenuHandler() {
317             @Override
318             public void onContextMenu(ContextMenuEvent event) {
319                 TreeView tree = app.getSelectedTree();
320                 if (tree != null && (tree.equals(app.getFolderTreeView()) || tree.equals(app.getOtherSharedTreeView()))) {
321                         Folder selectedFolder = app.getSelection();
322                         FileContextMenu contextMenu = new FileContextMenu(app, images, tree, selectedFolder, getSelectedFiles());
323                         int x = event.getNativeEvent().getClientX();
324                         int y = event.getNativeEvent().getClientY();
325                         contextMenu.setPopupPosition(x, y);
326                         contextMenu.show();
327                 }
328             }
329         }, ContextMenuEvent.getType());
330                 initWidget(vp);
331
332                 selectionModel = new MultiSelectionModel<File>(keyProvider);
333
334                 celltable.setSelectionModel(selectionModel, GSSSelectionEventManager.<File> createDefaultManager());
335 //              celltable.setPageSize(Pithos.VISIBLE_FILE_COUNT);
336                 
337                 sinkEvents(Event.ONCONTEXTMENU);
338 //              sinkEvents(Event.ONMOUSEUP);
339 //              sinkEvents(Event.ONMOUSEDOWN);
340 //              sinkEvents(Event.ONCLICK);
341 //              sinkEvents(Event.ONKEYDOWN);
342 //              sinkEvents(Event.ONDBLCLICK);
343                 Pithos.preventIESelection();
344         }
345
346         public List<File> getSelectedFiles() {
347         return new ArrayList<File>(selectionModel.getSelectedSet());
348         }
349         
350 //      @Override
351 //      public void onBrowserEvent(Event event) {
352 //
353 //              if (files == null || files.size() == 0) {
354 //                      if (DOM.eventGetType(event) == Event.ONCONTEXTMENU && getSelectedFiles().size() == 0) {
355 //                              contextMenu = new FileContextMenu(images, false, true);
356 //                contextMenu.show();
357 //                              event.preventDefault();
358 //                              event.cancelBubble(true);
359 //                      }
360 //                      return;
361 //              }
362 //              if (DOM.eventGetType(event) == Event.ONCONTEXTMENU && getSelectedFiles().size() != 0) {
363 //                      GWT.log("*****GOING TO SHOW CONTEXT MENU ****", null);
364 //                      contextMenu =  new FileContextMenu(images, false, false);
365 //                      contextMenu = contextMenu.onEvent(event);
366 //                      event.cancelBubble(true);
367 //                      event.preventDefault();
368 //              } else if (DOM.eventGetType(event) == Event.ONCONTEXTMENU && getSelectedFiles().size() == 0) {
369 //                      contextMenu = new FileContextMenu(images, false, true);
370 //                      contextMenu = contextMenu.onEmptyEvent(event);
371 //                      event.cancelBubble(true);
372 //                      event.preventDefault();
373 //              } else if (DOM.eventGetType(event) == Event.ONDBLCLICK)
374 //                      if (getSelectedFiles().size() == 1) {
375 //                              Pithos app = app;
376 //                              File file = getSelectedFiles().get(0);
377 //                              Window.open(file.getUri(), "_blank", "");
378 //                              event.preventDefault();
379 //                              return;
380 //                      }
381 //              super.onBrowserEvent(event);
382 //      }
383
384         /**
385          * Update the display of the file list.
386          */
387         void update(@SuppressWarnings("unused") boolean sort) {
388                 showCellTable();
389         }
390
391         /**
392          * Return the proper icon based on the MIME type of the file.
393          *
394          * @param file
395          * @return the icon
396          */
397         protected ImageResource getFileIcon(File file) {
398                 String mimetype = file.getContentType();
399                 boolean shared = file.isShared();
400                 if (mimetype == null)
401                         return shared ? images.documentShared() : images.document();
402                 mimetype = mimetype.toLowerCase();
403                 if (mimetype.startsWith("application/pdf"))
404                         return shared ? images.pdfShared() : images.pdf();
405                 else if (mimetype.endsWith("excel"))
406                         return shared ? images.spreadsheetShared() : images.spreadsheet();
407                 else if (mimetype.endsWith("msword"))
408                         return shared ? images.wordprocessorShared() : images.wordprocessor();
409                 else if (mimetype.endsWith("powerpoint"))
410                         return shared ? images.presentationShared() : images.presentation();
411                 else if (mimetype.startsWith("application/zip") ||
412                                         mimetype.startsWith("application/gzip") ||
413                                         mimetype.startsWith("application/x-gzip") ||
414                                         mimetype.startsWith("application/x-tar") ||
415                                         mimetype.startsWith("application/x-gtar"))
416                         return shared ? images.zipShared() : images.zip();
417                 else if (mimetype.startsWith("text/html"))
418                         return shared ? images.htmlShared() : images.html();
419                 else if (mimetype.startsWith("text/plain"))
420                         return shared ? images.txtShared() : images.txt();
421                 else if (mimetype.startsWith("image/"))
422                         return shared ? images.imageShared() : images.image();
423                 else if (mimetype.startsWith("video/"))
424                         return shared ? images.videoShared() : images.video();
425                 else if (mimetype.startsWith("audio/"))
426                         return shared ? images.audioShared() : images.audio();
427                 return shared ? images.documentShared() : images.document();
428         }
429
430         /**
431          * Fill the file cache with data.
432          */
433         public void setFiles(final List<File> _files) {
434                 files = new ArrayList<File>();
435         for (File fres : _files)
436                         files.add(fres);
437                 Collections.sort(files, new Comparator<File>() {
438
439                         @Override
440                         public int compare(File arg0, File arg1) {
441                                 return arg0.getName().compareTo(arg1.getName());
442                         }
443
444                 });
445                 folderFileCount = files.size();
446                 
447                 nameHeader.setSorted(true);
448                 nameHeader.toggleReverseSort();
449                 for (SortableHeader otherHeader : allHeaders) {
450                 if (otherHeader != nameHeader) {
451                     otherHeader.setSorted(false);
452                     otherHeader.setReverseSort(true);
453                 }
454             }
455
456         provider.setList(files);
457         selectionModel.clear();
458         app.showFolderStatistics(folderFileCount);
459         celltable.setPageSize(folderFileCount);
460         }
461
462         /**
463          * Does the list contains the requested filename
464          *
465          * @param fileName
466          * @return true/false
467          */
468         public boolean contains(String fileName) {
469                 for (int i = 0; i < files.size(); i++)
470                         if (files.get(i).getName().equals(fileName))
471                                 return true;
472                 return false;
473         }
474
475         public void clearSelectedRows() {
476                 Iterator<File> it = selectionModel.getSelectedSet().iterator();
477                 while(it.hasNext()){
478                         selectionModel.setSelected(it.next(),false);
479                 }
480         }
481         
482         /**
483          *
484          */
485         public void selectAllRows() {
486                 Iterator<File> it = provider.getList().iterator();
487                 while(it.hasNext()){
488                         selectionModel.setSelected(it.next(),true);
489                 }
490         }
491
492         protected void sortFiles(final String sortingProperty, final boolean sortingType){
493                 Collections.sort(files, new Comparator<File>() {
494
495             @Override
496             public int compare(File arg0, File arg1) {
497                     if (sortingType){
498                             if (sortingProperty.equals("version")) {
499                                     return arg0.getVersion() - arg1.getVersion();
500                             } else if (sortingProperty.equals("owner")) {
501                                     return arg0.getOwner().compareTo(arg1.getOwner());
502                             } else if (sortingProperty.equals("date")) {
503                                         if (arg0.getLastModified() != null && arg1.getLastModified() != null)
504                                                 return arg0.getLastModified().compareTo(arg1.getLastModified());
505                                         return 0;
506                             } else if (sortingProperty.equals("size")) {
507                                     return (int) (arg0.getBytes() - arg1.getBytes());
508                             } else if (sortingProperty.equals("name")) {
509                                     return arg0.getName().compareTo(arg1.getName());
510                             } else if (sortingProperty.equals("path")) {
511                                     return arg0.getUri().compareTo(arg1.getUri());
512                             } else {
513                                     return arg0.getName().compareTo(arg1.getName());
514                             }
515                     }
516                     else if (sortingProperty.equals("version")) {
517                             
518                             return arg1.getVersion() - arg0.getVersion();
519                     } else if (sortingProperty.equals("owner")) {
520                             
521                             return arg1.getOwner().compareTo(arg0.getOwner());
522                     } else if (sortingProperty.equals("date")) {
523                             
524                             return arg1.getLastModified().compareTo(arg0.getLastModified());
525                     } else if (sortingProperty.equals("size")) {
526                             return (int) (arg1.getBytes() - arg0.getBytes());
527                     } else if (sortingProperty.equals("name")) {
528                             
529                             return arg1.getName().compareTo(arg0.getName());
530                     } else if (sortingProperty.equals("path")) {
531                             
532                             return arg1.getUri().compareTo(arg0.getUri());
533                     } else {
534                             
535                             return arg1.getName().compareTo(arg0.getName());
536                     }
537             }
538
539                 });
540         }
541         
542         final class FileValueUpdater implements ValueUpdater<String>{
543                 private String property;
544                 private SortableHeader header;
545                 /**
546                  * 
547                  */
548                 public FileValueUpdater(SortableHeader header,String property) {
549                         this.property=property;
550                         this.header=header;
551                 }
552                 @Override
553                 public void update(@SuppressWarnings("unused") String value) {
554                         header.setSorted(true);
555                         header.toggleReverseSort();
556
557                 for (SortableHeader otherHeader : allHeaders) {
558                   if (otherHeader != header) {
559                     otherHeader.setSorted(false);
560                     otherHeader.setReverseSort(true);
561                   }
562                 }
563                 celltable.redrawHeaders();
564                 sortFiles(property, header.getReverseSort());
565                 FileList.this.update(true);                     
566                 }
567                 
568         }
569
570         /**
571          * Shows the files in the cellTable 
572      */
573         private void showCellTable(){
574                 provider.setList(files);
575                 
576                 provider.refresh();
577                 
578                 //celltable.redraw();
579                 celltable.redrawHeaders();              
580         }
581 }