Statistics
| Branch: | Tag: | Revision:

root / src / gr / grnet / pithos / web / client / Pithos.java @ e0a0c6ea

History | View | Annotate | Download (55.2 kB)

1
/*
2
 * Copyright 2011-2013 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
package gr.grnet.pithos.web.client;
36

    
37
import com.google.gwt.core.client.EntryPoint;
38
import com.google.gwt.core.client.GWT;
39
import com.google.gwt.core.client.JsArrayString;
40
import com.google.gwt.core.client.Scheduler;
41
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
42
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
43
import com.google.gwt.event.dom.client.ClickEvent;
44
import com.google.gwt.event.dom.client.ClickHandler;
45
import com.google.gwt.event.logical.shared.ResizeEvent;
46
import com.google.gwt.event.logical.shared.ResizeHandler;
47
import com.google.gwt.http.client.Response;
48
import com.google.gwt.http.client.URL;
49
import com.google.gwt.i18n.client.DateTimeFormat;
50
import com.google.gwt.i18n.client.Dictionary;
51
import com.google.gwt.i18n.client.TimeZone;
52
import com.google.gwt.resources.client.ClientBundle;
53
import com.google.gwt.resources.client.CssResource;
54
import com.google.gwt.resources.client.ImageResource;
55
import com.google.gwt.resources.client.ImageResource.ImageOptions;
56
import com.google.gwt.user.client.*;
57
import com.google.gwt.user.client.ui.*;
58
import com.google.gwt.view.client.SelectionChangeEvent;
59
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
60
import com.google.gwt.view.client.SingleSelectionModel;
61
import gr.grnet.pithos.web.client.catalog.UpdateUserCatalogs;
62
import gr.grnet.pithos.web.client.catalog.UserCatalogs;
63
import gr.grnet.pithos.web.client.commands.UploadFileCommand;
64
import gr.grnet.pithos.web.client.foldertree.*;
65
import gr.grnet.pithos.web.client.grouptree.Group;
66
import gr.grnet.pithos.web.client.grouptree.GroupTreeView;
67
import gr.grnet.pithos.web.client.grouptree.GroupTreeViewModel;
68
import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeView;
69
import gr.grnet.pithos.web.client.mysharedtree.MysharedTreeViewModel;
70
import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeView;
71
import gr.grnet.pithos.web.client.othersharedtree.OtherSharedTreeViewModel;
72
import gr.grnet.pithos.web.client.rest.*;
73
import org.apache.http.HttpStatus;
74

    
75
import java.util.*;
76

    
77
/**
78
 * Entry point classes define <code>onModuleLoad()</code>.
79
 */
80
public class Pithos implements EntryPoint, ResizeHandler {
81

    
82
    private static final boolean IsLOGEnabled = true;
83

    
84
    public static final Configuration config = GWT.create(Configuration.class);
85

    
86
    public interface Style extends CssResource {
87
        String commandAnchor();
88

    
89
        String statistics();
90

    
91
        @ClassName("gwt-HTML")
92
        String html();
93

    
94
        String uploadAlert();
95

    
96
        String uploadAlertLink();
97

    
98
        String uploadAlertProgress();
99

    
100
        String uploadAlertPercent();
101

    
102
        String uploadAlertClose();
103
    }
104

    
105
    public interface Resources extends ClientBundle {
106
        @Source("Pithos.css")
107
        Style pithosCss();
108

    
109
        @Source("gr/grnet/pithos/resources/close-popup.png")
110
        ImageResource closePopup();
111
    }
112

    
113
    public static Resources resources = GWT.create(Resources.class);
114

    
115
    /**
116
     * Instantiate an application-level image bundle. This object will provide
117
     * programmatic access to all the images needed by widgets.
118
     */
119
    static Images images = (Images) GWT.create(Images.class);
120

    
121
    public String getUserID() {
122
        return userID;
123
    }
124

    
125
    public UserCatalogs getUserCatalogs() {
126
        return userCatalogs;
127
    }
128

    
129
    public String getCurrentUserDisplayNameOrID() {
130
        final String displayName = userCatalogs.getDisplayName(getUserID());
131
        return displayName == null ? getUserID() : displayName;
132
    }
133

    
134
    public boolean hasDisplayNameForUserID(String userID) {
135
        return userCatalogs.getDisplayName(userID) != null;
136
    }
137

    
138
    public boolean hasIDForUserDisplayName(String userDisplayName) {
139
        return userCatalogs.getID(userDisplayName) != null;
140
    }
141

    
142
    public String getDisplayNameForUserID(String userID) {
143
        return userCatalogs.getDisplayName(userID);
144
    }
145

    
146
    public String getIDForUserDisplayName(String userDisplayName) {
147
        return userCatalogs.getID(userDisplayName);
148
    }
149

    
150
    public List<String> getDisplayNamesForUserIDs(List<String> userIDs) {
151
        if(userIDs == null) {
152
            userIDs = new ArrayList<String>();
153
        }
154
        final List<String> userDisplayNames = new ArrayList<String>();
155
        for(String userID : userIDs) {
156
            final String displayName = getDisplayNameForUserID(userID);
157
            userDisplayNames.add(displayName);
158
        }
159

    
160
        return userDisplayNames;
161
    }
162

    
163
    public List<String> filterUserIDsWithUnknownDisplayName(Collection<String> userIDs) {
164
        if(userIDs == null) {
165
            userIDs = new ArrayList<String>();
166
        }
167
        final List<String> filtered = new ArrayList<String>();
168
        for(String userID : userIDs) {
169
            if(!this.userCatalogs.hasID(userID)) {
170
                filtered.add(userID);
171
            }
172
        }
173
        return filtered;
174
    }
175

    
176
    public void setAccount(AccountResource acct) {
177
        account = acct;
178
    }
179

    
180
    public AccountResource getAccount() {
181
        return account;
182
    }
183

    
184
    public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
185
        folderTreeView.updateFolder(f, showfiles, callback, openParent);
186
    }
187

    
188
    public void updateGroupNode(Group group) {
189
        groupTreeView.updateGroupNode(group);
190
    }
191

    
192
    public void updateMySharedRoot() {
193
        mysharedTreeView.updateRoot();
194
    }
195

    
196
    public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
197
        mysharedTreeView.updateFolder(f, showfiles, callback);
198
    }
199

    
200
    public void updateSharedFolder(Folder f, boolean showfiles) {
201
        updateSharedFolder(f, showfiles, null);
202
    }
203

    
204
    public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
205
        otherSharedTreeView.updateFolder(f, showfiles, callback);
206
    }
207

    
208
    public MysharedTreeView getMySharedTreeView() {
209
        return mysharedTreeView;
210
    }
211

    
212
    /**
213
     * An aggregate image bundle that pulls together all the images for this
214
     * application into a single bundle.
215
     */
216
    public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
217

    
218
        @Source("gr/grnet/pithos/resources/document.png")
219
        ImageResource folders();
220

    
221
        @Source("gr/grnet/pithos/resources/advancedsettings.png")
222
        @ImageOptions(width = 32, height = 32)
223
        ImageResource tools();
224
    }
225

    
226
    private Throwable error;
227

    
228
    /**
229
     * The Application Clipboard implementation;
230
     */
231
    private Clipboard clipboard = new Clipboard();
232

    
233
    /**
234
     * The top panel that contains the menu bar.
235
     */
236
    private TopPanel topPanel;
237

    
238
    /**
239
     * The panel that contains the various system messages.
240
     */
241
    private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
242

    
243
    /**
244
     * The bottom panel that contains the status bar.
245
     */
246
    StatusPanel statusPanel = null;
247

    
248
    /**
249
     * The file list widget.
250
     */
251
    private FileList fileList;
252

    
253
    /**
254
     * The tab panel that occupies the right side of the screen.
255
     */
256
    private VerticalPanel inner = new VerticalPanel();
257

    
258

    
259
    /**
260
     * The split panel that will contain the left and right panels.
261
     */
262
    private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
263

    
264
    /**
265
     * The currently selected item in the application, for use by the Edit menu
266
     * commands. Potential types are Folder, File, User and Group.
267
     */
268
    private Object currentSelection;
269

    
270
    public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
271

    
272
    /**
273
     * The ID that uniquely identifies the user in Pithos+.
274
     * Currently this is a UUID. It used to be the user's email.
275
     */
276
    private String userID = null;
277

    
278
    /**
279
     * Hold mappings from user UUIDs to emails and vice-versa.
280
     */
281
    private UserCatalogs userCatalogs = new UserCatalogs();
282

    
283
    /**
284
     * The authentication token of the current user.
285
     */
286
    private String userToken;
287

    
288
    VerticalPanel trees;
289

    
290
    SingleSelectionModel<Folder> folderTreeSelectionModel;
291
    FolderTreeViewModel folderTreeViewModel;
292
    FolderTreeView folderTreeView;
293

    
294
    SingleSelectionModel<Folder> mysharedTreeSelectionModel;
295
    MysharedTreeViewModel mysharedTreeViewModel;
296
    MysharedTreeView mysharedTreeView = null;
297

    
298
    protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
299
    OtherSharedTreeViewModel otherSharedTreeViewModel;
300
    OtherSharedTreeView otherSharedTreeView = null;
301

    
302
    GroupTreeViewModel groupTreeViewModel;
303
    GroupTreeView groupTreeView;
304

    
305
    TreeView selectedTree;
306
    protected AccountResource account;
307

    
308
    Folder trash;
309

    
310
    List<Composite> treeViews = new ArrayList<Composite>();
311

    
312
    @SuppressWarnings("rawtypes")
313
    List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
314

    
315
    public Button upload;
316

    
317
    private HTML numOfFiles;
318

    
319
    private Toolbar toolbar;
320

    
321
    private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
322

    
323
    UploadAlert uploadAlert;
324

    
325
    Date lastModified;
326

    
327
    @Override
328
    public void onModuleLoad() {
329
        if(parseUserCredentials()) {
330
            initialize();
331
        }
332
    }
333

    
334
    static native void __ConsoleLog(String message) /*-{
335
      try {
336
        console.log(message);
337
      } catch (e) {
338
      }
339
    }-*/;
340

    
341
    public static void LOGError(Throwable error, StringBuilder sb) {
342
        if(!IsLOGEnabled) { return; }
343

    
344
        sb.append("\nException: [" + error.toString().replace("\n", "\n  ") + "]");
345
        Throwable cause = error.getCause();
346
        if(cause != null) {
347
            sb.append("\nCauses:\n");
348
            while(cause != null) {
349
                sb.append("  ");
350
                sb.append("[" + cause.toString().replace("\n", "\n  ")  + "]");
351
                sb.append("\n");
352
                cause = cause.getCause();
353
            }
354
        }
355
        else {
356
            sb.append("\n");
357
        }
358

    
359
        StackTraceElement[] stackTrace = error.getStackTrace();
360
        sb.append("Stack trace (" + stackTrace.length + " elements):\n");
361
        for(int i = 0; i < stackTrace.length; i++) {
362
            StackTraceElement errorElem = stackTrace[i];
363
            sb.append("  [" + i + "] ");
364
            sb.append(errorElem.toString());
365
            sb.append("\n");
366
        }
367
    }
368

    
369
    public static void LOGError(Throwable error) {
370
        if(!IsLOGEnabled) { return; }
371

    
372
        final StringBuilder sb = new StringBuilder();
373
        LOGError(error, sb);
374
        if(sb.length() > 0) {
375
            __ConsoleLog(sb.toString());
376
        }
377
    }
378

    
379
    public static boolean isLogEnabled() {
380
        return IsLOGEnabled;
381
    }
382

    
383
    public static void LOG(Object ...args) {
384
        if(!IsLOGEnabled) { return; }
385

    
386
        final StringBuilder sb = new StringBuilder();
387
        for(Object arg : args) {
388
            if(arg instanceof Throwable) {
389
                LOGError((Throwable) arg, sb);
390
            }
391
            else {
392
                sb.append(arg);
393
            }
394
        }
395

    
396
        if(sb.length() > 0) {
397
            __ConsoleLog(sb.toString());
398
        }
399
    }
400

    
401
    private void initialize() {
402
        lastModified = new Date(); //Initialize if-modified-since value with now.
403
        resources.pithosCss().ensureInjected();
404
        boolean bareContent = Window.Location.getParameter("noframe") != null;
405
        String contentWidth = bareContent ? Const.PERCENT_100 : "75%";
406

    
407
        VerticalPanel outer = new VerticalPanel();
408
        outer.setWidth(Const.PERCENT_100);
409
        if(!bareContent) {
410
            outer.addStyleName("pithos-outer");
411
        }
412

    
413
        if(!bareContent) {
414
            topPanel = new TopPanel(this, Pithos.images);
415
            topPanel.setWidth(Const.PERCENT_100);
416
            outer.add(topPanel);
417
            outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
418
        }
419

    
420
        messagePanel.setVisible(false);
421
        outer.add(messagePanel);
422
        outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
423
        outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
424

    
425
        HorizontalPanel header = new HorizontalPanel();
426
        header.addStyleName("pithos-header");
427
        header.setWidth(contentWidth);
428
        if(bareContent) {
429
            header.addStyleName("pithos-header-noframe");
430
        }
431
        upload = new Button("Upload", new ClickHandler() {
432
            @Override
433
            public void onClick(ClickEvent event) {
434
                if(getSelection() != null) {
435
                    new UploadFileCommand(Pithos.this, null, getSelection()).execute();
436
                }
437
            }
438
        });
439
        upload.addStyleName("pithos-uploadButton");
440
        header.add(upload);
441
        header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
442
        header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
443

    
444
        toolbar = new Toolbar(this);
445
        header.add(toolbar);
446
        header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
447
        header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
448

    
449
        HorizontalPanel folderStatistics = new HorizontalPanel();
450
        folderStatistics.addStyleName("pithos-folderStatistics");
451
        numOfFiles = new HTML();
452
        folderStatistics.add(numOfFiles);
453
        folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
454
        HTML numOfFilesLabel = new HTML("&nbsp;Files");
455
        folderStatistics.add(numOfFilesLabel);
456
        folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
457
        header.add(folderStatistics);
458
        header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
459
        header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
460
        header.setCellWidth(folderStatistics, "40px");
461
        outer.add(header);
462
        outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
463
        // Inner contains the various lists
464
        inner.sinkEvents(Event.ONCONTEXTMENU);
465
        inner.setWidth(Const.PERCENT_100);
466

    
467
        folderTreeSelectionModel = new SingleSelectionModel<Folder>();
468
        folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
469
            @Override
470
            public void onSelectionChange(SelectionChangeEvent event) {
471
                if(folderTreeSelectionModel.getSelectedObject() != null) {
472
                    deselectOthers(folderTreeView, folderTreeSelectionModel);
473
                    applyPermissions(folderTreeSelectionModel.getSelectedObject());
474
                    Folder f = folderTreeSelectionModel.getSelectedObject();
475
                    updateFolder(f, true, new Command() {
476

    
477
                        @Override
478
                        public void execute() {
479
                            updateStatistics();
480
                        }
481
                    }, true);
482
                    showRelevantToolbarButtons();
483
                }
484
                else {
485
                    if(getSelectedTree().equals(folderTreeView)) {
486
                        setSelectedTree(null);
487
                    }
488
                    if(getSelectedTree() == null) {
489
                        showRelevantToolbarButtons();
490
                    }
491
                }
492
            }
493
        });
494
        selectionModels.add(folderTreeSelectionModel);
495

    
496
        folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
497
        folderTreeView = new FolderTreeView(folderTreeViewModel);
498
        treeViews.add(folderTreeView);
499

    
500
        fileList = new FileList(this, images);
501
        inner.add(fileList);
502

    
503
        trees = new VerticalPanel();
504
        trees.setWidth(Const.PERCENT_100);
505

    
506
        // Add the left and right panels to the split panel.
507
        splitPanel.setLeftWidget(trees);
508
        FlowPanel right = new FlowPanel();
509
        right.getElement().setId("rightPanel");
510
        right.add(inner);
511
        splitPanel.setRightWidget(right);
512
        splitPanel.setSplitPosition("219px");
513
        splitPanel.setSize(Const.PERCENT_100, Const.PERCENT_100);
514
        splitPanel.addStyleName("pithos-splitPanel");
515
        splitPanel.setWidth(contentWidth);
516
        outer.add(splitPanel);
517
        outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
518

    
519
        if(!bareContent) {
520
            statusPanel = new StatusPanel();
521
            statusPanel.setWidth(Const.PERCENT_100);
522
            outer.add(statusPanel);
523
            outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
524
        }
525
        else {
526
            splitPanel.addStyleName("pithos-splitPanel-noframe");
527
        }
528

    
529
        // Hook the window resize event, so that we can adjust the UI.
530
        Window.addResizeHandler(this);
531
        // Clear out the window's built-in margin, because we want to take
532
        // advantage of the entire client area.
533
        Window.setMargin("0px");
534
        // Finally, add the outer panel to the RootPanel, so that it will be
535
        // displayed.
536
        RootPanel.get().add(outer);
537
        // Call the window resized handler to get the initial sizes setup. Doing
538
        // this in a deferred command causes it to occur after all widgets'
539
        // sizes have been computed by the browser.
540
        Scheduler.get().scheduleIncremental(new RepeatingCommand() {
541

    
542
            @Override
543
            public boolean execute() {
544
                if(!isCloudbarReady()) {
545
                    return true;
546
                }
547
                onWindowResized(Window.getClientHeight());
548
                return false;
549
            }
550
        });
551

    
552
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
553
            @Override
554
            public void execute() {
555
                LOG("Pithos::initialize() Calling Pithos::fetchAccount()");
556
                fetchAccount(new Command() {
557

    
558
                    @Override
559
                    public void execute() {
560
                        if(!account.hasHomeContainer()) {
561
                            createHomeContainer(account, this);
562
                        }
563
                        else if(!account.hasTrashContainer()) {
564
                            createTrashContainer(this);
565
                        }
566
                        else {
567
                            for(Folder f : account.getContainers()) {
568
                                if(f.getName().equals(Const.TRASH_CONTAINER)) {
569
                                    trash = f;
570
                                    break;
571
                                }
572
                            }
573
                            trees.add(folderTreeView);
574
                            folderTreeViewModel.initialize(account, new Command() {
575

    
576
                                @Override
577
                                public void execute() {
578
                                    createMySharedTree();
579
                                }
580
                            });
581

    
582
                            HorizontalPanel separator = new HorizontalPanel();
583
                            separator.addStyleName("pithos-statisticsSeparator");
584
                            separator.add(new HTML(""));
585
                            trees.add(separator);
586

    
587
                            groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
588
                            groupTreeView = new GroupTreeView(groupTreeViewModel);
589
                            treeViews.add(groupTreeView);
590
                            trees.add(groupTreeView);
591
                            folderTreeView.showStatistics(account);
592
                        }
593
                    }
594
                });
595
            }
596
        });
597
    }
598

    
599
    public void scheduleResfresh() {
600
        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
601

    
602
            @Override
603
            public boolean execute() {
604
                final Folder f = getSelection();
605
                if(f == null) {
606
                    return true;
607
                }
608

    
609
                HeadRequest<Folder> head = new HeadRequest<Folder>(Folder.class, getApiPath(), f.getOwnerID(), "/" + f.getContainer()) {
610

    
611
                    @Override
612
                    public void onSuccess(Folder _result) {
613
                        lastModified = new Date();
614
                        if(getSelectedTree().equals(folderTreeView)) {
615
                            updateFolder(f, true, new Command() {
616

    
617
                                @Override
618
                                public void execute() {
619
                                    scheduleResfresh();
620
                                }
621

    
622
                            }, false);
623
                        }
624
                        else if(getSelectedTree().equals(mysharedTreeView)) {
625
                            updateSharedFolder(f, true, new Command() {
626

    
627
                                @Override
628
                                public void execute() {
629
                                    scheduleResfresh();
630
                                }
631
                            });
632
                        }
633
                        else {
634
                            scheduleResfresh();
635
                        }
636
                    }
637

    
638
                    @Override
639
                    public void onError(Throwable t) {
640
                        if(t instanceof RestException && ((RestException) t).getHttpStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
641
                            scheduleResfresh();
642
                        }
643
                        else if(retries >= MAX_RETRIES) {
644
                            LOG("Error heading folder. ", t);
645
                            setError(t);
646
                            if(t instanceof RestException) {
647
                                displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
648
                            }
649
                            else {
650
                                displayError("System error heading folder: " + t.getMessage());
651
                            }
652
                        }
653
                        else {//retry
654
                            LOG("Retry ", retries);
655
                            Scheduler.get().scheduleDeferred(this);
656
                        }
657
                    }
658

    
659
                    @Override
660
                    protected void onUnauthorized(Response response) {
661
                        if(retries >= MAX_RETRIES) {
662
                            sessionExpired();
663
                        }
664
                        else //retry
665
                        {
666
                            Scheduler.get().scheduleDeferred(this);
667
                        }
668
                    }
669
                };
670
                head.setHeader(Const.X_AUTH_TOKEN, getUserToken());
671
                head.setHeader(Const.IF_MODIFIED_SINCE, DateTimeFormat.getFormat(Const.DATE_FORMAT_1).format(lastModified, TimeZone.createTimeZone(0)) + " GMT");
672
                Scheduler.get().scheduleDeferred(head);
673

    
674
                return false;
675
            }
676
        }, 3000);
677
    }
678

    
679
    public void applyPermissions(Folder f) {
680
        if(f != null) {
681
            if(f.isInTrash()) {
682
                upload.setEnabled(false);
683
                disableUploadArea();
684
            }
685
            else {
686
                Boolean[] perms = f.getPermissions().get(userID);
687
                if(f.getOwnerID().equals(userID) || (perms != null && perms[1] != null && perms[1])) {
688
                    upload.setEnabled(true);
689
                    enableUploadArea();
690
                }
691
                else {
692
                    upload.setEnabled(false);
693
                    disableUploadArea();
694
                }
695
            }
696
        }
697
        else {
698
            upload.setEnabled(false);
699
            disableUploadArea();
700
        }
701
    }
702

    
703
    @SuppressWarnings({"rawtypes", "unchecked"})
704
    public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
705
        selectedTree = _selectedTree;
706

    
707
        for(SingleSelectionModel s : selectionModels) {
708
            if(!s.equals(model) && s.getSelectedObject() != null) {
709
                s.setSelected(s.getSelectedObject(), false);
710
            }
711
        }
712
    }
713

    
714
    public void showFiles(final Folder f) {
715
        Set<File> files = f.getFiles();
716
        showFiles(files);
717
    }
718

    
719
    public void showFiles(Set<File> files) {
720
        fileList.setFiles(new ArrayList<File>(files));
721
    }
722

    
723
    /**
724
     * Parse and store the user credentials to the appropriate fields.
725
     */
726
    private boolean parseUserCredentials() {
727
        Configuration conf = (Configuration) GWT.create(Configuration.class);
728
        Dictionary otherProperties = Dictionary.getDictionary(Const.OTHER_PROPERTIES);
729
        String cookie = otherProperties.get(Const.AUTH_COOKIE);
730
        String auth = Cookies.getCookie(cookie);
731
        if(auth == null) {
732
            authenticateUser();
733
            return false;
734
        }
735
        if(auth.startsWith("\"")) {
736
            auth = auth.substring(1);
737
        }
738
        if(auth.endsWith("\"")) {
739
            auth = auth.substring(0, auth.length() - 1);
740
        }
741
        String[] authSplit = auth.split("\\" + conf.cookieSeparator(), 2);
742
        if(authSplit.length != 2) {
743
            authenticateUser();
744
            return false;
745
        }
746
        userID = authSplit[0];
747
        userToken = authSplit[1];
748

    
749
        String gotoUrl = Window.Location.getParameter("goto");
750
        if(gotoUrl != null && gotoUrl.length() > 0) {
751
            Window.Location.assign(gotoUrl);
752
            return false;
753
        }
754
        return true;
755
    }
756

    
757
    /**
758
     * Redirect the user to the login page for authentication.
759
     */
760
    protected void authenticateUser() {
761
        Dictionary otherProperties = Dictionary.getDictionary(Const.OTHER_PROPERTIES);
762
        Window.Location.assign(otherProperties.get(Const.LOGIN_URL) + Window.Location.getHref());
763
    }
764

    
765
    public void fetchAccount(final Command callback) {
766
        String path = "?format=json";
767

    
768
        GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getApiPath(), userID, path) {
769
            @Override
770
            public void onSuccess(AccountResource accountResource) {
771
                account = accountResource;
772
                if(callback != null) {
773
                    callback.execute();
774
                }
775

    
776
                final List<String> memberIDs = new ArrayList<String>();
777
                final List<Group> groups = account.getGroups();
778
                for(Group group : groups) {
779
                    memberIDs.addAll(group.getMemberIDs());
780
                }
781
                memberIDs.add(Pithos.this.getUserID());
782

    
783
                final List<String> theUnknown = Pithos.this.filterUserIDsWithUnknownDisplayName(memberIDs);
784
                // Initialize the user catalog
785
                new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();
786
                LOG("Called new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();");
787
            }
788

    
789
            @Override
790
            public void onError(Throwable t) {
791
                LOG("Error getting account", t);
792
                setError(t);
793
                if(t instanceof RestException) {
794
                    displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
795
                }
796
                else {
797
                    displayError("System error fetching user data: " + t.getMessage());
798
                }
799
            }
800

    
801
            @Override
802
            protected void onUnauthorized(Response response) {
803
                sessionExpired();
804
            }
805
        };
806
        getAccount.setHeader(Const.X_AUTH_TOKEN, userToken);
807
        Scheduler.get().scheduleDeferred(getAccount);
808
    }
809

    
810
    public void updateStatistics() {
811
        HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getApiPath(), userID, "", account) {
812

    
813
            @Override
814
            public void onSuccess(AccountResource _result) {
815
                folderTreeView.showStatistics(account);
816
            }
817

    
818
            @Override
819
            public void onError(Throwable t) {
820
                LOG("Error getting account", t);
821
                setError(t);
822
                if(t instanceof RestException) {
823
                    displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
824
                }
825
                else {
826
                    displayError("System error fetching user data: " + t.getMessage());
827
                }
828
            }
829

    
830
            @Override
831
            protected void onUnauthorized(Response response) {
832
                sessionExpired();
833
            }
834
        };
835
        headAccount.setHeader(Const.X_AUTH_TOKEN, userToken);
836
        Scheduler.get().scheduleDeferred(headAccount);
837
    }
838

    
839
    protected void createHomeContainer(final AccountResource _account, final Command callback) {
840
        String path = "/" + Const.HOME_CONTAINER;
841
        PutRequest createPithos = new PutRequest(getApiPath(), getUserID(), path) {
842
            @Override
843
            public void onSuccess(Resource result) {
844
                if(!_account.hasTrashContainer()) {
845
                    createTrashContainer(callback);
846
                }
847
                else {
848
                    fetchAccount(callback);
849
                }
850
            }
851

    
852
            @Override
853
            public void onError(Throwable t) {
854
                LOG("Error creating pithos", t);
855
                setError(t);
856
                if(t instanceof RestException) {
857
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
858
                }
859
                else {
860
                    displayError("System error Error creating pithos: " + t.getMessage());
861
                }
862
            }
863

    
864
            @Override
865
            protected void onUnauthorized(Response response) {
866
                sessionExpired();
867
            }
868
        };
869
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
870
        Scheduler.get().scheduleDeferred(createPithos);
871
    }
872

    
873
    protected void createTrashContainer(final Command callback) {
874
        String path = "/" + Const.TRASH_CONTAINER;
875
        PutRequest createPithos = new PutRequest(getApiPath(), getUserID(), path) {
876
            @Override
877
            public void onSuccess(Resource result) {
878
                fetchAccount(callback);
879
            }
880

    
881
            @Override
882
            public void onError(Throwable t) {
883
                LOG("Error creating pithos", t);
884
                setError(t);
885
                if(t instanceof RestException) {
886
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
887
                }
888
                else {
889
                    displayError("System error Error creating pithos: " + t.getMessage());
890
                }
891
            }
892

    
893
            @Override
894
            protected void onUnauthorized(Response response) {
895
                sessionExpired();
896
            }
897
        };
898
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
899
        Scheduler.get().scheduleDeferred(createPithos);
900
    }
901

    
902
    /**
903
     * Creates an HTML fragment that places an image & caption together, for use
904
     * in a group header.
905
     *
906
     * @param imageProto an image prototype for an image
907
     * @param caption    the group caption
908
     * @return the header HTML fragment
909
     */
910
    private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
911
        String captionHTML = "<table class='caption' cellpadding='0' "
912
            + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
913
            + "</td><td id =" + caption + " class='rcaption'><b style='white-space:nowrap'>&nbsp;"
914
            + caption + "</b></td></tr></table>";
915
        return captionHTML;
916
    }
917

    
918
    protected void onWindowResized(int height) {
919
        // Adjust the split panel to take up the available room in the window.
920
        int newHeight = height - splitPanel.getAbsoluteTop() - 153;
921
        if(newHeight < 1) {
922
            newHeight = 1;
923
        }
924
        splitPanel.setHeight("" + newHeight);
925
        inner.setHeight("" + newHeight);
926
    }
927

    
928
    native boolean isCloudbarReady()/*-{
929
      if ($wnd.$("div.cloudbar") && $wnd.$("div.cloudbar").height() > 0)
930
        return true;
931
      return false;
932
    }-*/;
933

    
934
    @Override
935
    public void onResize(ResizeEvent event) {
936
        int height = event.getHeight();
937
        onWindowResized(height);
938
    }
939

    
940
    /**
941
     * Display an error message.
942
     *
943
     * @param msg the message to display
944
     */
945
    public void displayError(String msg) {
946
        messagePanel.displayError(msg);
947
        onWindowResized(Window.getClientHeight());
948
    }
949

    
950
    /**
951
     * Display a warning message.
952
     *
953
     * @param msg the message to display
954
     */
955
    public void displayWarning(String msg) {
956
        messagePanel.displayWarning(msg);
957
        onWindowResized(Window.getClientHeight());
958
    }
959

    
960
    /**
961
     * Display an informational message.
962
     *
963
     * @param msg the message to display
964
     */
965
    public void displayInformation(String msg) {
966
        messagePanel.displayInformation(msg);
967
        onWindowResized(Window.getClientHeight());
968
    }
969

    
970
    /**
971
     * Retrieve the fileList.
972
     *
973
     * @return the fileList
974
     */
975
    public FileList getFileList() {
976
        return fileList;
977
    }
978

    
979
    /**
980
     * Retrieve the topPanel.
981
     *
982
     * @return the topPanel
983
     */
984
    TopPanel getTopPanel() {
985
        return topPanel;
986
    }
987

    
988
    /**
989
     * Retrieve the clipboard.
990
     *
991
     * @return the clipboard
992
     */
993
    public Clipboard getClipboard() {
994
        return clipboard;
995
    }
996

    
997
    public StatusPanel getStatusPanel() {
998
        return statusPanel;
999
    }
1000

    
1001
    public String getUserToken() {
1002
        return userToken;
1003
    }
1004

    
1005
    public static native void preventIESelection() /*-{
1006
      $doc.body.onselectstart = function () {
1007
        return false;
1008
      };
1009
    }-*/;
1010

    
1011
    public static native void enableIESelection() /*-{
1012
      if ($doc.body.onselectstart != null)
1013
        $doc.body.onselectstart = null;
1014
    }-*/;
1015

    
1016
    /**
1017
     * @return the absolute path of the API root URL
1018
     */
1019
    public String getApiPath() {
1020
        Configuration conf = (Configuration) GWT.create(Configuration.class);
1021
        return conf.apiPath();
1022
    }
1023

    
1024
    /**
1025
     * History support for folder navigation
1026
     * adds a new browser history entry
1027
     *
1028
     * @param key
1029
     */
1030
    public void updateHistory(String key) {
1031
//                Replace any whitespace of the initial string to "+"
1032
//                String result = key.replaceAll("\\s","+");
1033
//                Add a new browser history entry.
1034
//                History.newItem(result);
1035
        History.newItem(key);
1036
    }
1037

    
1038
    public void deleteFolder(final Folder folder, final Command callback) {
1039
        final PleaseWaitPopup pwp = new PleaseWaitPopup();
1040
        pwp.center();
1041
        String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
1042
        DeleteRequest deleteFolder = new DeleteRequest(getApiPath(), folder.getOwnerID(), path) {
1043

    
1044
            @Override
1045
            protected void onUnauthorized(Response response) {
1046
                pwp.hide();
1047
                sessionExpired();
1048
            }
1049

    
1050
            @Override
1051
            public void onSuccess(Resource result) {
1052
                updateFolder(folder.getParent(), true, new Command() {
1053

    
1054
                    @Override
1055
                    public void execute() {
1056
                        folderTreeSelectionModel.setSelected(folder.getParent(), true);
1057
                        updateStatistics();
1058
                        if(callback != null) {
1059
                            callback.execute();
1060
                        }
1061
                        pwp.hide();
1062
                    }
1063
                }, true);
1064
            }
1065

    
1066
            @Override
1067
            public void onError(Throwable t) {
1068
                LOG(t);
1069
                setError(t);
1070
                if(t instanceof RestException) {
1071
                    if(((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND) {
1072
                        displayError("Unable to delete folder: " + ((RestException) t).getHttpStatusText());
1073
                    }
1074
                    else {
1075
                        onSuccess(null);
1076
                    }
1077
                }
1078
                else {
1079
                    displayError("System error unable to delete folder: " + t.getMessage());
1080
                }
1081
                pwp.hide();
1082
            }
1083
        };
1084
        deleteFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1085
        Scheduler.get().scheduleDeferred(deleteFolder);
1086
    }
1087

    
1088
    public FolderTreeView getFolderTreeView() {
1089
        return folderTreeView;
1090
    }
1091

    
1092
    public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
1093
        if(iter.hasNext()) {
1094
            File file = iter.next();
1095
            String path = targetUri + "/" + file.getName();
1096
            PutRequest copyFile = new PutRequest(getApiPath(), targetUsername, path) {
1097
                @Override
1098
                public void onSuccess(Resource result) {
1099
                    copyFiles(iter, targetUsername, targetUri, callback);
1100
                }
1101

    
1102
                @Override
1103
                public void onError(Throwable t) {
1104
                    LOG(t);
1105
                    setError(t);
1106
                    if(t instanceof RestException) {
1107
                        displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
1108
                    }
1109
                    else {
1110
                        displayError("System error unable to copy file: " + t.getMessage());
1111
                    }
1112
                }
1113

    
1114
                @Override
1115
                protected void onUnauthorized(Response response) {
1116
                    sessionExpired();
1117
                }
1118
            };
1119
            copyFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1120
            copyFile.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(file.getUri()));
1121
            if(!file.getOwnerID().equals(targetUsername)) {
1122
                copyFile.setHeader(Const.X_SOURCE_ACCOUNT, URL.encodePathSegment(file.getOwnerID()));
1123
            }
1124
            copyFile.setHeader(Const.CONTENT_TYPE, file.getContentType());
1125
            Scheduler.get().scheduleDeferred(copyFile);
1126
        }
1127
        else if(callback != null) {
1128
            callback.execute();
1129
        }
1130
    }
1131

    
1132
    public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
1133
        String path = targetUri + "?delimiter=/";
1134
        PutRequest copyFolder = new PutRequest(getApiPath(), targetUsername, path) {
1135
            @Override
1136
            public void onSuccess(Resource result) {
1137
                if(callback != null) {
1138
                    callback.execute();
1139
                }
1140
            }
1141

    
1142
            @Override
1143
            public void onError(Throwable t) {
1144
                LOG(t);
1145
                setError(t);
1146
                if(t instanceof RestException) {
1147
                    displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1148
                }
1149
                else {
1150
                    displayError("System error copying folder: " + t.getMessage());
1151
                }
1152
            }
1153

    
1154
            @Override
1155
            protected void onUnauthorized(Response response) {
1156
                sessionExpired();
1157
            }
1158
        };
1159
        copyFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1160
        copyFolder.setHeader(Const.ACCEPT, "*/*");
1161
        copyFolder.setHeader(Const.CONTENT_LENGTH, "0");
1162
        copyFolder.setHeader(Const.CONTENT_TYPE, "application/directory");
1163
        if(!f.getOwnerID().equals(targetUsername)) {
1164
            copyFolder.setHeader(Const.X_SOURCE_ACCOUNT, f.getOwnerID());
1165
        }
1166
        if(move) {
1167
            copyFolder.setHeader(Const.X_MOVE_FROM, URL.encodePathSegment(f.getUri()));
1168
        }
1169
        else {
1170
            copyFolder.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(f.getUri()));
1171
        }
1172
        Scheduler.get().scheduleDeferred(copyFolder);
1173
    }
1174

    
1175
    public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1176
        selectionModels.add(model);
1177
    }
1178

    
1179
    public OtherSharedTreeView getOtherSharedTreeView() {
1180
        return otherSharedTreeView;
1181
    }
1182

    
1183
    public void updateTrash(boolean showFiles, Command callback) {
1184
        updateFolder(trash, showFiles, callback, true);
1185
    }
1186

    
1187
    public void updateGroupsNode() {
1188
        groupTreeView.updateGroupNode(null);
1189
    }
1190

    
1191
    public Group addGroup(String groupname) {
1192
        Group newGroup = new Group(groupname);
1193
        account.addGroup(newGroup);
1194
        groupTreeView.updateGroupNode(null);
1195
        return newGroup;
1196
    }
1197

    
1198
    public void removeGroup(Group group) {
1199
        account.removeGroup(group);
1200
        updateGroupsNode();
1201
    }
1202

    
1203
    public TreeView getSelectedTree() {
1204
        return selectedTree;
1205
    }
1206

    
1207
    public void setSelectedTree(TreeView selected) {
1208
        selectedTree = selected;
1209
    }
1210

    
1211
    public Folder getSelection() {
1212
        if(selectedTree != null) {
1213
            return selectedTree.getSelection();
1214
        }
1215
        return null;
1216
    }
1217

    
1218
    public void showFolderStatistics(int folderFileCount) {
1219
        numOfFiles.setHTML(String.valueOf(folderFileCount));
1220
    }
1221

    
1222
    public GroupTreeView getGroupTreeView() {
1223
        return groupTreeView;
1224
    }
1225

    
1226
    public void sessionExpired() {
1227
        new SessionExpiredDialog(this).center();
1228
    }
1229

    
1230
    public void updateRootFolder(Command callback) {
1231
        updateFolder(account.getPithos(), false, callback, true);
1232
    }
1233

    
1234
    void createMySharedTree() {
1235
        LOG("Pithos::createMySharedTree()");
1236
        mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1237
        mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1238
            @Override
1239
            public void onSelectionChange(SelectionChangeEvent event) {
1240
                if(mysharedTreeSelectionModel.getSelectedObject() != null) {
1241
                    deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1242
                    upload.setEnabled(false);
1243
                    disableUploadArea();
1244
                    updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1245
                    showRelevantToolbarButtons();
1246
                }
1247
                else {
1248
                    if(getSelectedTree().equals(mysharedTreeView)) {
1249
                        setSelectedTree(null);
1250
                    }
1251
                    if(getSelectedTree() == null) {
1252
                        showRelevantToolbarButtons();
1253
                    }
1254
                }
1255
            }
1256
        });
1257
        selectionModels.add(mysharedTreeSelectionModel);
1258
        mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1259
        mysharedTreeViewModel.initialize(new Command() {
1260

    
1261
            @Override
1262
            public void execute() {
1263
                mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1264
                trees.insert(mysharedTreeView, 2);
1265
                treeViews.add(mysharedTreeView);
1266
                createOtherSharedTree();
1267
            }
1268
        });
1269
    }
1270

    
1271
    void createOtherSharedTree() {
1272
        LOG("Pithos::createOtherSharedTree()");
1273
        otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1274
        otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1275
            @Override
1276
            public void onSelectionChange(SelectionChangeEvent event) {
1277
                if(otherSharedTreeSelectionModel.getSelectedObject() != null) {
1278
                    deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1279
                    applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1280
                    updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1281
                    showRelevantToolbarButtons();
1282
                }
1283
                else {
1284
                    if(getSelectedTree().equals(otherSharedTreeView)) {
1285
                        setSelectedTree(null);
1286
                    }
1287
                    if(getSelectedTree() == null) {
1288
                        showRelevantToolbarButtons();
1289
                    }
1290
                }
1291
            }
1292
        });
1293
        selectionModels.add(otherSharedTreeSelectionModel);
1294
        otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1295
        LOG("Pithos::createOtherSharedTree(), initializing otherSharedTreeViewModel with a callback");
1296
        otherSharedTreeViewModel.initialize(new Command() {
1297
            @Override
1298
            public void execute() {
1299
                otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1300
                trees.insert(otherSharedTreeView, 1);
1301
                treeViews.add(otherSharedTreeView);
1302
                scheduleResfresh();
1303
            }
1304
        });
1305
    }
1306

    
1307
    public String getErrorData() {
1308
        final StringBuilder sb = new StringBuilder();
1309
        final String NL = Const.NL;
1310
        Throwable t = this.error;
1311
        while(t != null) {
1312
            sb.append(t.toString());
1313
            sb.append(NL);
1314
            StackTraceElement[] traces = t.getStackTrace();
1315
            for(StackTraceElement trace : traces) {
1316
                sb.append("  [");
1317
                sb.append(trace.getClassName());
1318
                sb.append("::");
1319
                sb.append(trace.getMethodName());
1320
                sb.append("() at ");
1321
                sb.append(trace.getFileName());
1322
                sb.append(":");
1323
                sb.append(trace.getLineNumber());
1324
                sb.append("]");
1325
                sb.append(NL);
1326
            }
1327
            t = t.getCause();
1328
        }
1329

    
1330
        return sb.toString();
1331
    }
1332

    
1333
    public void setError(Throwable t) {
1334
        error = t;
1335
        LOG(t);
1336
    }
1337

    
1338
    public void showRelevantToolbarButtons() {
1339
        toolbar.showRelevantButtons();
1340
    }
1341

    
1342
    public FileUploadDialog getFileUploadDialog() {
1343
        if(fileUploadDialog == null) {
1344
            fileUploadDialog = new FileUploadDialog(this);
1345
        }
1346
        return fileUploadDialog;
1347
    }
1348

    
1349
    public void hideUploadIndicator() {
1350
        upload.removeStyleName("pithos-uploadButton-loading");
1351
        upload.setTitle("");
1352
    }
1353

    
1354
    public void showUploadIndicator() {
1355
        upload.addStyleName("pithos-uploadButton-loading");
1356
        upload.setTitle("Upload in progress. Click for details.");
1357
    }
1358

    
1359
    public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1360
        if(folder == null) {
1361
            if(callback != null) {
1362
                callback.execute();
1363
            }
1364
        }
1365
        else {
1366
            HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getApiPath(), folder.getOwnerID(), folder.getUri(), folder) {
1367

    
1368
                @Override
1369
                public void onSuccess(Folder _result) {
1370
                    if(callback != null) {
1371
                        callback.execute();
1372
                    }
1373
                }
1374

    
1375
                @Override
1376
                public void onError(Throwable t) {
1377
                    if(t instanceof RestException) {
1378
                        if(((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1379
                            final String path = folder.getUri();
1380
                            PutRequest newFolder = new PutRequest(getApiPath(), folder.getOwnerID(), path) {
1381
                                @Override
1382
                                public void onSuccess(Resource _result) {
1383
                                    scheduleFolderHeadCommand(folder, callback);
1384
                                }
1385

    
1386
                                @Override
1387
                                public void onError(Throwable _t) {
1388
                                    setError(_t);
1389
                                    if(_t instanceof RestException) {
1390
                                        displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1391
                                    }
1392
                                    else {
1393
                                        displayError("System error creating folder: " + _t.getMessage());
1394
                                    }
1395
                                }
1396

    
1397
                                @Override
1398
                                protected void onUnauthorized(Response response) {
1399
                                    sessionExpired();
1400
                                }
1401
                            };
1402
                            newFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1403
                            newFolder.setHeader(Const.CONTENT_TYPE, "application/folder");
1404
                            newFolder.setHeader(Const.ACCEPT, "*/*");
1405
                            newFolder.setHeader(Const.CONTENT_LENGTH, "0");
1406
                            Scheduler.get().scheduleDeferred(newFolder);
1407
                        }
1408
                        else if(((RestException) t).getHttpStatusCode() == Response.SC_FORBIDDEN) {
1409
                            onSuccess(folder);
1410
                        }
1411
                        else {
1412
                            displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1413
                        }
1414
                    }
1415
                    else {
1416
                        displayError("System error heading folder: " + t.getMessage());
1417
                    }
1418

    
1419
                    LOG("Error heading folder", t);
1420
                    setError(t);
1421
                }
1422

    
1423
                @Override
1424
                protected void onUnauthorized(Response response) {
1425
                    sessionExpired();
1426
                }
1427
            };
1428
            headFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1429
            Scheduler.get().scheduleDeferred(headFolder);
1430
        }
1431
    }
1432

    
1433
    public void scheduleFileHeadCommand(File f, final Command callback) {
1434
        HeadRequest<File> headFile = new HeadRequest<File>(File.class, getApiPath(), f.getOwnerID(), f.getUri(), f) {
1435

    
1436
            @Override
1437
            public void onSuccess(File _result) {
1438
                if(callback != null) {
1439
                    callback.execute();
1440
                }
1441
            }
1442

    
1443
            @Override
1444
            public void onError(Throwable t) {
1445
                LOG("Error heading file", t);
1446
                setError(t);
1447
                if(t instanceof RestException) {
1448
                    displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1449
                }
1450
                else {
1451
                    displayError("System error heading file: " + t.getMessage());
1452
                }
1453
            }
1454

    
1455
            @Override
1456
            protected void onUnauthorized(Response response) {
1457
                sessionExpired();
1458
            }
1459
        };
1460
        headFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1461
        Scheduler.get().scheduleDeferred(headFile);
1462
    }
1463

    
1464
    public boolean isMySharedSelected() {
1465
        return getSelectedTree().equals(getMySharedTreeView());
1466
    }
1467

    
1468
    private Folder getUploadFolder() {
1469
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1470
            return getSelection();
1471
        }
1472
        return null;
1473
    }
1474

    
1475
    private void updateUploadFolder() {
1476
        updateUploadFolder(null);
1477
    }
1478

    
1479
    private void updateUploadFolder(final JsArrayString urls) {
1480
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1481
            Folder f = getSelection();
1482
            if(getSelectedTree().equals(getFolderTreeView())) {
1483
                updateFolder(f, true, new Command() {
1484

    
1485
                    @Override
1486
                    public void execute() {
1487
                        updateStatistics();
1488
                        if(urls != null) {
1489
                            selectUploadedFiles(urls);
1490
                        }
1491
                    }
1492
                }, false);
1493
            }
1494
            else {
1495
                updateOtherSharedFolder(f, true, null);
1496
            }
1497
        }
1498
    }
1499

    
1500
    public native void disableUploadArea() /*-{
1501
      var uploader = $wnd.$("#uploader").pluploadQueue();
1502
      var dropElm = $wnd.document.getElementById('rightPanel');
1503
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1504
    }-*/;
1505

    
1506
    public native void enableUploadArea() /*-{
1507
      var uploader = $wnd.$("#uploader").pluploadQueue();
1508
      var dropElm = $wnd.document.getElementById('rightPanel');
1509
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1510
      if (uploader.runtime == 'html5') {
1511
        uploader.settings.drop_element = 'rightPanel';
1512
        uploader.trigger('PostInit');
1513
      }
1514
    }-*/;
1515

    
1516
    public void showUploadAlert(int nOfFiles) {
1517
        if(uploadAlert == null) {
1518
            uploadAlert = new UploadAlert(this, nOfFiles);
1519
        }
1520
        if(!uploadAlert.isShowing()) {
1521
            uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1522

    
1523
                @Override
1524
                public void setPosition(int offsetWidth, int offsetHeight) {
1525
                    uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth) / 2, statusPanel.getAbsoluteTop() - offsetHeight);
1526
                }
1527
            });
1528
        }
1529
        uploadAlert.setNumOfFiles(nOfFiles);
1530
    }
1531

    
1532
    public void hideUploadAlert() {
1533
        if(uploadAlert != null && uploadAlert.isShowing()) {
1534
            uploadAlert.hide();
1535
        }
1536
    }
1537

    
1538
    public void selectUploadedFiles(JsArrayString urls) {
1539
        List<String> selectedUrls = new ArrayList<String>();
1540
        for(int i = 0; i < urls.length(); i++) {
1541
            selectedUrls.add(urls.get(i));
1542
        }
1543
        fileList.selectByUrl(selectedUrls);
1544
    }
1545

    
1546
    public void purgeContainer(final Folder container) {
1547
        String path = "/" + container.getName() + "?delimiter=/";
1548
        DeleteRequest delete = new DeleteRequest(getApiPath(), getUserID(), path) {
1549

    
1550
            @Override
1551
            protected void onUnauthorized(Response response) {
1552
                sessionExpired();
1553
            }
1554

    
1555
            @Override
1556
            public void onSuccess(Resource result) {
1557
                updateFolder(container, true, null, true);
1558
            }
1559

    
1560
            @Override
1561
            public void onError(Throwable t) {
1562
                LOG("Error deleting trash", t);
1563
                setError(t);
1564
                if(t instanceof RestException) {
1565
                    displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1566
                }
1567
                else {
1568
                    displayError("System error deleting trash: " + t.getMessage());
1569
                }
1570
            }
1571
        };
1572
        delete.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1573
        Scheduler.get().scheduleDeferred(delete);
1574
    }
1575
}