Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (58.1 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
    private static final boolean IsLOGEnabled = false;
82
    public static final boolean IsDetailedHTTPLOGEnabled = true;
83
    public static final boolean IsFullResponseBodyLOGEnabled = true;
84

    
85
    public static final Set<String> HTTPHeadersToIgnoreInLOG = new HashSet<String>();
86
    static {
87
        HTTPHeadersToIgnoreInLOG.add(Const.HTTP_HEADER_CONNECTION);
88
        HTTPHeadersToIgnoreInLOG.add(Const.HTTP_HEADER_DATE);
89
        HTTPHeadersToIgnoreInLOG.add(Const.HTTP_HEADER_KEEP_ALIVE);
90
        HTTPHeadersToIgnoreInLOG.add(Const.HTTP_HEADER_SERVER);
91
        HTTPHeadersToIgnoreInLOG.add(Const.HTTP_HEADER_VARY);
92
        HTTPHeadersToIgnoreInLOG.add(Const.IF_MODIFIED_SINCE);
93
    }
94

    
95
    public static final Configuration config = GWT.create(Configuration.class);
96
    public static final String CONFIG_API_PATH = config.apiPath();
97
    static {
98
        LOG("CONFIG_API_PATH = ", CONFIG_API_PATH);
99
    }
100

    
101
    public static final Dictionary otherProperties = Dictionary.getDictionary(Const.OTHER_PROPERTIES);
102
    public static String getFromOtherPropertiesOrNull(String key) {
103
        try {
104
            return otherProperties.get(key);
105
        }
106
        catch(Exception e) {
107
            LOGError(e);
108
            return null;
109
        }
110
    }
111
    public static final String OTHERPROPS_STORAGE_API_URL = getFromOtherPropertiesOrNull("STORAGE_API_URL");
112
    public static final String OTHERPROPS_USER_CATALOGS_API_URL = getFromOtherPropertiesOrNull("USER_CATALOGS_API_URL");
113
    static {
114
        LOG("STORAGE_API_URL = ", OTHERPROPS_STORAGE_API_URL);
115
        LOG("USER_CATALOGS_API_URL = ", OTHERPROPS_USER_CATALOGS_API_URL);
116
    }
117

    
118
    public static final String STORAGE_API_URL;
119
    static {
120
        if(OTHERPROPS_STORAGE_API_URL != null) {
121
            STORAGE_API_URL = OTHERPROPS_STORAGE_API_URL;
122
        }
123
        else if(CONFIG_API_PATH != null) {
124
            STORAGE_API_URL = CONFIG_API_PATH;
125
        }
126
        else {
127
            throw new RuntimeException("Unknown STORAGE_API_URL");
128
        }
129
    }
130
    public static final String USER_CATALOGS_API_URL;
131
    static {
132
        if(OTHERPROPS_USER_CATALOGS_API_URL != null) {
133
            USER_CATALOGS_API_URL = OTHERPROPS_USER_CATALOGS_API_URL;
134
        }
135
        else if(OTHERPROPS_STORAGE_API_URL != null) {
136
            throw new RuntimeException("STORAGE_API_URL is defined but USER_CATALOGS_API_URL is not");
137
        }
138
        else {
139
            // https://server.com/v1/ --> https://server.com
140
            String url = CONFIG_API_PATH;
141
            url = Helpers.stripTrailing(url, "/");
142
            url = Helpers.upToIncludingLastPart(url, "/");
143
            url = Helpers.stripTrailing(url, "/");
144
            url = url + "/user_catalogs";
145

    
146
            USER_CATALOGS_API_URL = url;
147

    
148
            LOG("USER_CATALOGS_API_URL = ", USER_CATALOGS_API_URL);
149
        }
150
    }
151

    
152
    public interface Style extends CssResource {
153
        String commandAnchor();
154

    
155
        String statistics();
156

    
157
        @ClassName("gwt-HTML")
158
        String html();
159

    
160
        String uploadAlert();
161

    
162
        String uploadAlertLink();
163

    
164
        String uploadAlertProgress();
165

    
166
        String uploadAlertPercent();
167

    
168
        String uploadAlertClose();
169
    }
170

    
171
    public interface Resources extends ClientBundle {
172
        @Source("Pithos.css")
173
        Style pithosCss();
174

    
175
        @Source("gr/grnet/pithos/resources/close-popup.png")
176
        ImageResource closePopup();
177
    }
178

    
179
    public static Resources resources = GWT.create(Resources.class);
180

    
181
    /**
182
     * Instantiate an application-level image bundle. This object will provide
183
     * programmatic access to all the images needed by widgets.
184
     */
185
    static Images images = (Images) GWT.create(Images.class);
186

    
187
    public String getUserID() {
188
        return userID;
189
    }
190

    
191
    public UserCatalogs getUserCatalogs() {
192
        return userCatalogs;
193
    }
194

    
195
    public String getCurrentUserDisplayNameOrID() {
196
        final String displayName = userCatalogs.getDisplayName(getUserID());
197
        return displayName == null ? getUserID() : displayName;
198
    }
199

    
200
    public boolean hasDisplayNameForUserID(String userID) {
201
        return userCatalogs.getDisplayName(userID) != null;
202
    }
203

    
204
    public boolean hasIDForUserDisplayName(String userDisplayName) {
205
        return userCatalogs.getID(userDisplayName) != null;
206
    }
207

    
208
    public String getDisplayNameForUserID(String userID) {
209
        return userCatalogs.getDisplayName(userID);
210
    }
211

    
212
    public String getIDForUserDisplayName(String userDisplayName) {
213
        return userCatalogs.getID(userDisplayName);
214
    }
215

    
216
    public List<String> getDisplayNamesForUserIDs(List<String> userIDs) {
217
        if(userIDs == null) {
218
            userIDs = new ArrayList<String>();
219
        }
220
        final List<String> userDisplayNames = new ArrayList<String>();
221
        for(String userID : userIDs) {
222
            final String displayName = getDisplayNameForUserID(userID);
223
            userDisplayNames.add(displayName);
224
        }
225

    
226
        return userDisplayNames;
227
    }
228

    
229
    public List<String> filterUserIDsWithUnknownDisplayName(Collection<String> userIDs) {
230
        if(userIDs == null) {
231
            userIDs = new ArrayList<String>();
232
        }
233
        final List<String> filtered = new ArrayList<String>();
234
        for(String userID : userIDs) {
235
            if(!this.userCatalogs.hasID(userID)) {
236
                filtered.add(userID);
237
            }
238
        }
239
        return filtered;
240
    }
241

    
242
    public void setAccount(AccountResource acct) {
243
        account = acct;
244
    }
245

    
246
    public AccountResource getAccount() {
247
        return account;
248
    }
249

    
250
    public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
251
        folderTreeView.updateFolder(f, showfiles, callback, openParent);
252
    }
253

    
254
    public void updateGroupNode(Group group) {
255
        groupTreeView.updateGroupNode(group);
256
    }
257

    
258
    public void updateMySharedRoot() {
259
        mysharedTreeView.updateRoot();
260
    }
261

    
262
    public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
263
        mysharedTreeView.updateFolder(f, showfiles, callback);
264
    }
265

    
266
    public void updateSharedFolder(Folder f, boolean showfiles) {
267
        updateSharedFolder(f, showfiles, null);
268
    }
269

    
270
    public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
271
        otherSharedTreeView.updateFolder(f, showfiles, callback);
272
    }
273

    
274
    public MysharedTreeView getMySharedTreeView() {
275
        return mysharedTreeView;
276
    }
277

    
278
    /**
279
     * An aggregate image bundle that pulls together all the images for this
280
     * application into a single bundle.
281
     */
282
    public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
283

    
284
        @Source("gr/grnet/pithos/resources/document.png")
285
        ImageResource folders();
286

    
287
        @Source("gr/grnet/pithos/resources/advancedsettings.png")
288
        @ImageOptions(width = 32, height = 32)
289
        ImageResource tools();
290
    }
291

    
292
    private Throwable error;
293

    
294
    /**
295
     * The Application Clipboard implementation;
296
     */
297
    private Clipboard clipboard = new Clipboard();
298

    
299
    /**
300
     * The top panel that contains the menu bar.
301
     */
302
    private TopPanel topPanel;
303

    
304
    /**
305
     * The panel that contains the various system messages.
306
     */
307
    private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
308

    
309
    /**
310
     * The bottom panel that contains the status bar.
311
     */
312
    StatusPanel statusPanel = null;
313

    
314
    /**
315
     * The file list widget.
316
     */
317
    private FileList fileList;
318

    
319
    /**
320
     * The tab panel that occupies the right side of the screen.
321
     */
322
    private VerticalPanel inner = new VerticalPanel();
323

    
324

    
325
    /**
326
     * The split panel that will contain the left and right panels.
327
     */
328
    private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
329

    
330
    /**
331
     * The currently selected item in the application, for use by the Edit menu
332
     * commands. Potential types are Folder, File, User and Group.
333
     */
334
    private Object currentSelection;
335

    
336
    public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
337

    
338
    /**
339
     * The ID that uniquely identifies the user in Pithos+.
340
     * Currently this is a UUID. It used to be the user's email.
341
     */
342
    private String userID = null;
343

    
344
    /**
345
     * Holds mappings from user UUIDs to emails and vice-versa.
346
     */
347
    private UserCatalogs userCatalogs = new UserCatalogs();
348

    
349
    /**
350
     * The authentication token of the current user.
351
     */
352
    private String userToken;
353

    
354
    VerticalPanel trees;
355

    
356
    SingleSelectionModel<Folder> folderTreeSelectionModel;
357
    FolderTreeViewModel folderTreeViewModel;
358
    FolderTreeView folderTreeView;
359

    
360
    SingleSelectionModel<Folder> mysharedTreeSelectionModel;
361
    MysharedTreeViewModel mysharedTreeViewModel;
362
    MysharedTreeView mysharedTreeView = null;
363

    
364
    protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
365
    OtherSharedTreeViewModel otherSharedTreeViewModel;
366
    OtherSharedTreeView otherSharedTreeView = null;
367

    
368
    GroupTreeViewModel groupTreeViewModel;
369
    GroupTreeView groupTreeView;
370

    
371
    TreeView selectedTree;
372
    protected AccountResource account;
373

    
374
    Folder trash;
375

    
376
    List<Composite> treeViews = new ArrayList<Composite>();
377

    
378
    @SuppressWarnings("rawtypes")
379
    List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
380

    
381
    public Button upload;
382

    
383
    private HTML numOfFiles;
384

    
385
    private Toolbar toolbar;
386

    
387
    private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
388

    
389
    UploadAlert uploadAlert;
390

    
391
    Date lastModified;
392

    
393
    @Override
394
    public void onModuleLoad() {
395
        if(parseUserCredentials()) {
396
            initialize();
397
        }
398
    }
399

    
400
    static native void __ConsoleLog(String message) /*-{
401
      try { console.log(message); } catch (e) {}
402
    }-*/;
403

    
404
    public static void LOGError(Throwable error, StringBuilder sb) {
405
        if(!isLOGEnabled()) { return; }
406

    
407
        sb.append("\nException: [" + error.toString().replace("\n", "\n  ") + "]");
408
        Throwable cause = error.getCause();
409
        if(cause != null) {
410
            sb.append("\nCauses:\n");
411
            while(cause != null) {
412
                sb.append("  ");
413
                sb.append("[" + cause.toString().replace("\n", "\n  ")  + "]");
414
                sb.append("\n");
415
                cause = cause.getCause();
416
            }
417
        }
418
        else {
419
            sb.append("\n");
420
        }
421

    
422
        StackTraceElement[] stackTrace = error.getStackTrace();
423
        sb.append("Stack trace (" + stackTrace.length + " elements):\n");
424
        for(int i = 0; i < stackTrace.length; i++) {
425
            StackTraceElement errorElem = stackTrace[i];
426
            sb.append("  [" + i + "] ");
427
            sb.append(errorElem.toString());
428
            sb.append("\n");
429
        }
430
    }
431

    
432
    public static void LOGError(Throwable error) {
433
        if(!isLOGEnabled()) { return; }
434

    
435
        final StringBuilder sb = new StringBuilder();
436
        LOGError(error, sb);
437
        if(sb.length() > 0) {
438
            __ConsoleLog(sb.toString());
439
        }
440
    }
441

    
442
    public static boolean isLOGEnabled() {
443
        return IsLOGEnabled;
444
    }
445

    
446
    public static void LOG(Object ...args) {
447
        if(!isLOGEnabled()) { return; }
448

    
449
        final StringBuilder sb = new StringBuilder();
450
        for(Object arg : args) {
451
            if(arg instanceof Throwable) {
452
                LOGError((Throwable) arg, sb);
453
            }
454
            else {
455
                sb.append(arg);
456
            }
457
        }
458

    
459
        if(sb.length() > 0) {
460
            __ConsoleLog(sb.toString());
461
        }
462
    }
463

    
464
    private void initialize() {
465
        userCatalogs.updateWithIDAndName("*", "All Pithos users");
466

    
467
        lastModified = new Date(); //Initialize if-modified-since value with now.
468
        resources.pithosCss().ensureInjected();
469
        boolean bareContent = Window.Location.getParameter("noframe") != null;
470
        String contentWidth = bareContent ? Const.PERCENT_100 : Const.PERCENT_75;
471

    
472
        VerticalPanel outer = new VerticalPanel();
473
        outer.setWidth(Const.PERCENT_100);
474
        if(!bareContent) {
475
            outer.addStyleName("pithos-outer");
476
        }
477

    
478
        if(!bareContent) {
479
            topPanel = new TopPanel(this, Pithos.images);
480
            topPanel.setWidth(Const.PERCENT_100);
481
            outer.add(topPanel);
482
            outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
483
        }
484

    
485
        messagePanel.setVisible(false);
486
        outer.add(messagePanel);
487
        outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
488
        outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
489

    
490
        HorizontalPanel header = new HorizontalPanel();
491
        header.addStyleName("pithos-header");
492
        header.setWidth(contentWidth);
493
        if(bareContent) {
494
            header.addStyleName("pithos-header-noframe");
495
        }
496
        upload = new Button("Upload", new ClickHandler() {
497
            @Override
498
            public void onClick(ClickEvent event) {
499
                if(getSelection() != null) {
500
                    new UploadFileCommand(Pithos.this, null, getSelection()).execute();
501
                }
502
            }
503
        });
504
        upload.addStyleName("pithos-uploadButton");
505
        header.add(upload);
506
        header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
507
        header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
508

    
509
        toolbar = new Toolbar(this);
510
        header.add(toolbar);
511
        header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
512
        header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
513

    
514
        HorizontalPanel folderStatistics = new HorizontalPanel();
515
        folderStatistics.addStyleName("pithos-folderStatistics");
516
        numOfFiles = new HTML();
517
        folderStatistics.add(numOfFiles);
518
        folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
519
        HTML numOfFilesLabel = new HTML("&nbsp;Files");
520
        folderStatistics.add(numOfFilesLabel);
521
        folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
522
        header.add(folderStatistics);
523
        header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
524
        header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
525
        header.setCellWidth(folderStatistics, "40px");
526
        outer.add(header);
527
        outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
528
        // Inner contains the various lists
529
        inner.sinkEvents(Event.ONCONTEXTMENU);
530
        inner.setWidth(Const.PERCENT_100);
531

    
532
        folderTreeSelectionModel = new SingleSelectionModel<Folder>();
533
        folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
534
            @Override
535
            public void onSelectionChange(SelectionChangeEvent event) {
536
                if(folderTreeSelectionModel.getSelectedObject() != null) {
537
                    deselectOthers(folderTreeView, folderTreeSelectionModel);
538
                    applyPermissions(folderTreeSelectionModel.getSelectedObject());
539
                    Folder f = folderTreeSelectionModel.getSelectedObject();
540
                    updateFolder(f, true, new Command() {
541

    
542
                        @Override
543
                        public void execute() {
544
                            updateStatistics();
545
                        }
546
                    }, true);
547
                    showRelevantToolbarButtons();
548
                }
549
                else {
550
                    if(getSelectedTree().equals(folderTreeView)) {
551
                        setSelectedTree(null);
552
                    }
553
                    if(getSelectedTree() == null) {
554
                        showRelevantToolbarButtons();
555
                    }
556
                }
557
            }
558
        });
559
        selectionModels.add(folderTreeSelectionModel);
560

    
561
        folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
562
        folderTreeView = new FolderTreeView(folderTreeViewModel);
563
        treeViews.add(folderTreeView);
564

    
565
        fileList = new FileList(this, images);
566
        inner.add(fileList);
567

    
568
        trees = new VerticalPanel();
569
        trees.setWidth(Const.PERCENT_100);
570

    
571
        // Add the left and right panels to the split panel.
572
        splitPanel.setLeftWidget(trees);
573
        FlowPanel right = new FlowPanel();
574
        right.getElement().setId("rightPanel");
575
        right.add(inner);
576
        splitPanel.setRightWidget(right);
577
        splitPanel.setSplitPosition("219px");
578
        splitPanel.setSize(Const.PERCENT_100, Const.PERCENT_100);
579
        splitPanel.addStyleName("pithos-splitPanel");
580
        splitPanel.setWidth(contentWidth);
581
        outer.add(splitPanel);
582
        outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
583

    
584
        if(!bareContent) {
585
            statusPanel = new StatusPanel();
586
            statusPanel.setWidth(Const.PERCENT_100);
587
            outer.add(statusPanel);
588
            outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
589
        }
590
        else {
591
            splitPanel.addStyleName("pithos-splitPanel-noframe");
592
        }
593

    
594
        // Hook the window resize event, so that we can adjust the UI.
595
        Window.addResizeHandler(this);
596
        // Clear out the window's built-in margin, because we want to take
597
        // advantage of the entire client area.
598
        Window.setMargin("0px");
599
        // Finally, add the outer panel to the RootPanel, so that it will be
600
        // displayed.
601
        RootPanel.get().add(outer);
602
        // Call the window resized handler to get the initial sizes setup. Doing
603
        // this in a deferred command causes it to occur after all widgets'
604
        // sizes have been computed by the browser.
605
        Scheduler.get().scheduleIncremental(new RepeatingCommand() {
606

    
607
            @Override
608
            public boolean execute() {
609
                if(!isCloudbarReady()) {
610
                    return true;
611
                }
612
                onWindowResized(Window.getClientHeight());
613
                return false;
614
            }
615
        });
616

    
617
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
618
            @Override
619
            public void execute() {
620
                LOG("Pithos::initialize() Calling Pithos::fetchAccount()");
621
                fetchAccount(new Command() {
622

    
623
                    @Override
624
                    public void execute() {
625
                        if(!account.hasHomeContainer()) {
626
                            createHomeContainer(account, this);
627
                        }
628
                        else if(!account.hasTrashContainer()) {
629
                            createTrashContainer(this);
630
                        }
631
                        else {
632
                            for(Folder f : account.getContainers()) {
633
                                if(f.getName().equals(Const.TRASH_CONTAINER)) {
634
                                    trash = f;
635
                                    break;
636
                                }
637
                            }
638
                            trees.add(folderTreeView);
639
                            folderTreeViewModel.initialize(account, new Command() {
640

    
641
                                @Override
642
                                public void execute() {
643
                                    createMySharedTree();
644
                                }
645
                            });
646

    
647
                            HorizontalPanel separator = new HorizontalPanel();
648
                            separator.addStyleName("pithos-statisticsSeparator");
649
                            separator.add(new HTML(""));
650
                            trees.add(separator);
651

    
652
                            groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
653
                            groupTreeView = new GroupTreeView(groupTreeViewModel);
654
                            treeViews.add(groupTreeView);
655
                            trees.add(groupTreeView);
656
                            folderTreeView.showStatistics(account);
657
                        }
658
                    }
659
                });
660
            }
661
        });
662
    }
663

    
664
    public void scheduleResfresh() {
665
        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
666

    
667
            @Override
668
            public boolean execute() {
669
                final Folder f = getSelection();
670
                if(f == null) {
671
                    return true;
672
                }
673

    
674
                HeadRequest<Folder> head = new HeadRequest<Folder>(Folder.class, getStorageAPIURL(), f.getOwnerID(), "/" + f.getContainer()) {
675

    
676
                    @Override
677
                    public void onSuccess(Folder _result) {
678
                        lastModified = new Date();
679
                        if(getSelectedTree().equals(folderTreeView)) {
680
                            updateFolder(f, true, new Command() {
681

    
682
                                @Override
683
                                public void execute() {
684
                                    scheduleResfresh();
685
                                }
686

    
687
                            }, false);
688
                        }
689
                        else if(getSelectedTree().equals(mysharedTreeView)) {
690
                            updateSharedFolder(f, true, new Command() {
691

    
692
                                @Override
693
                                public void execute() {
694
                                    scheduleResfresh();
695
                                }
696
                            });
697
                        }
698
                        else {
699
                            scheduleResfresh();
700
                        }
701
                    }
702

    
703
                    @Override
704
                    public void onError(Throwable t) {
705
                        if(t instanceof RestException && ((RestException) t).getHttpStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
706
                            scheduleResfresh();
707
                        }
708
                        else if(retries >= MAX_RETRIES) {
709
                            LOG("Error heading folder. ", t);
710
                            setError(t);
711
                            if(t instanceof RestException) {
712
                                displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
713
                            }
714
                            else {
715
                                displayError("System error heading folder: " + t.getMessage());
716
                            }
717
                        }
718
                        else {//retry
719
                            LOG("Retry ", retries);
720
                            Scheduler.get().scheduleDeferred(this);
721
                        }
722
                    }
723

    
724
                    @Override
725
                    protected void onUnauthorized(Response response) {
726
                        if(retries >= MAX_RETRIES) {
727
                            sessionExpired();
728
                        }
729
                        else //retry
730
                        {
731
                            Scheduler.get().scheduleDeferred(this);
732
                        }
733
                    }
734
                };
735
                head.setHeader(Const.X_AUTH_TOKEN, getUserToken());
736
                head.setHeader(Const.IF_MODIFIED_SINCE, DateTimeFormat.getFormat(Const.DATE_FORMAT_1).format(lastModified, TimeZone.createTimeZone(0)) + " GMT");
737
                Scheduler.get().scheduleDeferred(head);
738

    
739
                return false;
740
            }
741
        }, 3000);
742
    }
743

    
744
    public void applyPermissions(Folder f) {
745
        if(f != null) {
746
            if(f.isInTrash()) {
747
                upload.setEnabled(false);
748
                disableUploadArea();
749
            }
750
            else {
751
                Boolean[] perms = f.getPermissions().get(userID);
752
                if(f.getOwnerID().equals(userID) || (perms != null && perms[1] != null && perms[1])) {
753
                    upload.setEnabled(true);
754
                    enableUploadArea();
755
                }
756
                else {
757
                    upload.setEnabled(false);
758
                    disableUploadArea();
759
                }
760
            }
761
        }
762
        else {
763
            upload.setEnabled(false);
764
            disableUploadArea();
765
        }
766
    }
767

    
768
    @SuppressWarnings({"rawtypes", "unchecked"})
769
    public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
770
        selectedTree = _selectedTree;
771

    
772
        for(SingleSelectionModel s : selectionModels) {
773
            if(!s.equals(model) && s.getSelectedObject() != null) {
774
                s.setSelected(s.getSelectedObject(), false);
775
            }
776
        }
777
    }
778

    
779
    public void showFiles(final Folder f) {
780
        Set<File> files = f.getFiles();
781
        showFiles(files);
782
    }
783

    
784
    public void showFiles(Set<File> files) {
785
        fileList.setFiles(new ArrayList<File>(files));
786
    }
787

    
788
    /**
789
     * Parse and store the user credentials to the appropriate fields.
790
     */
791
    private boolean parseUserCredentials() {
792
        final String cookie = otherProperties.get(Const.AUTH_COOKIE);
793
        String auth = Cookies.getCookie(cookie);
794
        if(auth == null) {
795
            authenticateUser();
796
            return false;
797
        }
798
        if(auth.startsWith("\"")) {
799
            auth = auth.substring(1);
800
        }
801
        if(auth.endsWith("\"")) {
802
            auth = auth.substring(0, auth.length() - 1);
803
        }
804
        String[] authSplit = auth.split("\\" + config.cookieSeparator(), 2);
805
        if(authSplit.length != 2) {
806
            authenticateUser();
807
            return false;
808
        }
809
        this.userID = authSplit[0];
810
        this.userToken = authSplit[1];
811

    
812
        String gotoUrl = Window.Location.getParameter("goto");
813
        if(gotoUrl != null && gotoUrl.length() > 0) {
814
            Window.Location.assign(gotoUrl);
815
            return false;
816
        }
817
        return true;
818
    }
819

    
820
    /**
821
     * Redirect the user to the login page for authentication.
822
     */
823
    protected void authenticateUser() {
824
        Dictionary otherProperties = Dictionary.getDictionary(Const.OTHER_PROPERTIES);
825
        Window.Location.assign(otherProperties.get(Const.LOGIN_URL) + Window.Location.getHref());
826
    }
827

    
828
    public void fetchAccount(final Command callback) {
829
        String path = "?format=json";
830

    
831
        GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getStorageAPIURL(), userID, path) {
832
            @Override
833
            public void onSuccess(AccountResource accountResource) {
834
                account = accountResource;
835
                if(callback != null) {
836
                    callback.execute();
837
                }
838

    
839
                final List<String> memberIDs = new ArrayList<String>();
840
                final List<Group> groups = account.getGroups();
841
                for(Group group : groups) {
842
                    memberIDs.addAll(group.getMemberIDs());
843
                }
844
                memberIDs.add(Pithos.this.getUserID());
845

    
846
                final List<String> theUnknown = Pithos.this.filterUserIDsWithUnknownDisplayName(memberIDs);
847
                // Initialize the user catalog
848
                new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();
849
                LOG("Called new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();");
850
            }
851

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

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

    
873
    public void updateStatistics() {
874
        HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getStorageAPIURL(), userID, "", account) {
875

    
876
            @Override
877
            public void onSuccess(AccountResource _result) {
878
                folderTreeView.showStatistics(account);
879
            }
880

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

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

    
902
    protected void createHomeContainer(final AccountResource _account, final Command callback) {
903
        String path = "/" + Const.HOME_CONTAINER;
904
        PutRequest createPithos = new PutRequest(getStorageAPIURL(), getUserID(), path) {
905
            @Override
906
            public void onSuccess(Resource result) {
907
                if(!_account.hasTrashContainer()) {
908
                    createTrashContainer(callback);
909
                }
910
                else {
911
                    fetchAccount(callback);
912
                }
913
            }
914

    
915
            @Override
916
            public void onError(Throwable t) {
917
                LOG("Error creating pithos", t);
918
                setError(t);
919
                if(t instanceof RestException) {
920
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
921
                }
922
                else {
923
                    displayError("System error Error creating pithos: " + t.getMessage());
924
                }
925
            }
926

    
927
            @Override
928
            protected void onUnauthorized(Response response) {
929
                sessionExpired();
930
            }
931
        };
932
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
933
        Scheduler.get().scheduleDeferred(createPithos);
934
    }
935

    
936
    protected void createTrashContainer(final Command callback) {
937
        String path = "/" + Const.TRASH_CONTAINER;
938
        PutRequest createPithos = new PutRequest(getStorageAPIURL(), getUserID(), path) {
939
            @Override
940
            public void onSuccess(Resource result) {
941
                fetchAccount(callback);
942
            }
943

    
944
            @Override
945
            public void onError(Throwable t) {
946
                LOG("Error creating pithos", t);
947
                setError(t);
948
                if(t instanceof RestException) {
949
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
950
                }
951
                else {
952
                    displayError("System error Error creating pithos: " + t.getMessage());
953
                }
954
            }
955

    
956
            @Override
957
            protected void onUnauthorized(Response response) {
958
                sessionExpired();
959
            }
960
        };
961
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
962
        Scheduler.get().scheduleDeferred(createPithos);
963
    }
964

    
965
    /**
966
     * Creates an HTML fragment that places an image & caption together, for use
967
     * in a group header.
968
     *
969
     * @param imageProto an image prototype for an image
970
     * @param caption    the group caption
971
     * @return the header HTML fragment
972
     */
973
    private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
974
        String captionHTML = "<table class='caption' cellpadding='0' "
975
            + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
976
            + "</td><td id =" + caption + " class='rcaption'><b style='white-space:nowrap'>&nbsp;"
977
            + caption + "</b></td></tr></table>";
978
        return captionHTML;
979
    }
980

    
981
    protected void onWindowResized(int height) {
982
        // Adjust the split panel to take up the available room in the window.
983
        int newHeight = height - splitPanel.getAbsoluteTop() - 153;
984
        if(newHeight < 1) {
985
            newHeight = 1;
986
        }
987
        splitPanel.setHeight("" + newHeight);
988
        inner.setHeight("" + newHeight);
989
    }
990

    
991
    native boolean isCloudbarReady()/*-{
992
      if ($wnd.$("div.cloudbar") && $wnd.$("div.cloudbar").height() > 0)
993
        return true;
994
      return false;
995
    }-*/;
996

    
997
    @Override
998
    public void onResize(ResizeEvent event) {
999
        int height = event.getHeight();
1000
        onWindowResized(height);
1001
    }
1002

    
1003
    /**
1004
     * Display an error message.
1005
     *
1006
     * @param msg the message to display
1007
     */
1008
    public void displayError(String msg) {
1009
        messagePanel.displayError(msg);
1010
        onWindowResized(Window.getClientHeight());
1011
    }
1012

    
1013
    /**
1014
     * Display a warning message.
1015
     *
1016
     * @param msg the message to display
1017
     */
1018
    public void displayWarning(String msg) {
1019
        messagePanel.displayWarning(msg);
1020
        onWindowResized(Window.getClientHeight());
1021
    }
1022

    
1023
    /**
1024
     * Display an informational message.
1025
     *
1026
     * @param msg the message to display
1027
     */
1028
    public void displayInformation(String msg) {
1029
        messagePanel.displayInformation(msg);
1030
        onWindowResized(Window.getClientHeight());
1031
    }
1032

    
1033
    /**
1034
     * Retrieve the fileList.
1035
     *
1036
     * @return the fileList
1037
     */
1038
    public FileList getFileList() {
1039
        return fileList;
1040
    }
1041

    
1042
    /**
1043
     * Retrieve the topPanel.
1044
     *
1045
     * @return the topPanel
1046
     */
1047
    TopPanel getTopPanel() {
1048
        return topPanel;
1049
    }
1050

    
1051
    /**
1052
     * Retrieve the clipboard.
1053
     *
1054
     * @return the clipboard
1055
     */
1056
    public Clipboard getClipboard() {
1057
        return clipboard;
1058
    }
1059

    
1060
    public StatusPanel getStatusPanel() {
1061
        return statusPanel;
1062
    }
1063

    
1064
    public String getUserToken() {
1065
        return userToken;
1066
    }
1067

    
1068
    public static native void preventIESelection() /*-{
1069
      $doc.body.onselectstart = function () {
1070
        return false;
1071
      };
1072
    }-*/;
1073

    
1074
    public static native void enableIESelection() /*-{
1075
      if ($doc.body.onselectstart != null)
1076
        $doc.body.onselectstart = null;
1077
    }-*/;
1078

    
1079
    public static String getStorageAPIURL() {
1080
        return STORAGE_API_URL;
1081
    }
1082

    
1083
    public static String getUserCatalogsURL() {
1084
        return USER_CATALOGS_API_URL;
1085
    }
1086

    
1087
    /**
1088
     * History support for folder navigation
1089
     * adds a new browser history entry
1090
     *
1091
     * @param key
1092
     */
1093
    public void updateHistory(String key) {
1094
//                Replace any whitespace of the initial string to "+"
1095
//                String result = key.replaceAll("\\s","+");
1096
//                Add a new browser history entry.
1097
//                History.newItem(result);
1098
        History.newItem(key);
1099
    }
1100

    
1101
    public void deleteFolder(final Folder folder, final Command callback) {
1102
        final PleaseWaitPopup pwp = new PleaseWaitPopup();
1103
        pwp.center();
1104
        String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
1105
        DeleteRequest deleteFolder = new DeleteRequest(getStorageAPIURL(), folder.getOwnerID(), path) {
1106

    
1107
            @Override
1108
            protected void onUnauthorized(Response response) {
1109
                pwp.hide();
1110
                sessionExpired();
1111
            }
1112

    
1113
            @Override
1114
            public void onSuccess(Resource result) {
1115
                updateFolder(folder.getParent(), true, new Command() {
1116

    
1117
                    @Override
1118
                    public void execute() {
1119
                        folderTreeSelectionModel.setSelected(folder.getParent(), true);
1120
                        updateStatistics();
1121
                        if(callback != null) {
1122
                            callback.execute();
1123
                        }
1124
                        pwp.hide();
1125
                    }
1126
                }, true);
1127
            }
1128

    
1129
            @Override
1130
            public void onError(Throwable t) {
1131
                LOG(t);
1132
                setError(t);
1133
                if(t instanceof RestException) {
1134
                    if(((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND) {
1135
                        displayError("Unable to delete folder: " + ((RestException) t).getHttpStatusText());
1136
                    }
1137
                    else {
1138
                        onSuccess(null);
1139
                    }
1140
                }
1141
                else {
1142
                    displayError("System error unable to delete folder: " + t.getMessage());
1143
                }
1144
                pwp.hide();
1145
            }
1146
        };
1147
        deleteFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1148
        Scheduler.get().scheduleDeferred(deleteFolder);
1149
    }
1150

    
1151
    public FolderTreeView getFolderTreeView() {
1152
        return folderTreeView;
1153
    }
1154

    
1155
    public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
1156
        if(iter.hasNext()) {
1157
            File file = iter.next();
1158
            String path = targetUri + "/" + file.getName();
1159
            PutRequest copyFile = new PutRequest(getStorageAPIURL(), targetUsername, path) {
1160
                @Override
1161
                public void onSuccess(Resource result) {
1162
                    copyFiles(iter, targetUsername, targetUri, callback);
1163
                }
1164

    
1165
                @Override
1166
                public void onError(Throwable t) {
1167
                    LOG(t);
1168
                    setError(t);
1169
                    if(t instanceof RestException) {
1170
                        displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
1171
                    }
1172
                    else {
1173
                        displayError("System error unable to copy file: " + t.getMessage());
1174
                    }
1175
                }
1176

    
1177
                @Override
1178
                protected void onUnauthorized(Response response) {
1179
                    sessionExpired();
1180
                }
1181
            };
1182
            copyFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1183
            copyFile.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(file.getUri()));
1184
            if(!file.getOwnerID().equals(targetUsername)) {
1185
                copyFile.setHeader(Const.X_SOURCE_ACCOUNT, URL.encodePathSegment(file.getOwnerID()));
1186
            }
1187
            copyFile.setHeader(Const.CONTENT_TYPE, file.getContentType());
1188
            Scheduler.get().scheduleDeferred(copyFile);
1189
        }
1190
        else if(callback != null) {
1191
            callback.execute();
1192
        }
1193
    }
1194

    
1195
    public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
1196
        String path = targetUri + "?delimiter=/";
1197
        PutRequest copyFolder = new PutRequest(getStorageAPIURL(), targetUsername, path) {
1198
            @Override
1199
            public void onSuccess(Resource result) {
1200
                if(callback != null) {
1201
                    callback.execute();
1202
                }
1203
            }
1204

    
1205
            @Override
1206
            public void onError(Throwable t) {
1207
                LOG(t);
1208
                setError(t);
1209
                if(t instanceof RestException) {
1210
                    displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1211
                }
1212
                else {
1213
                    displayError("System error copying folder: " + t.getMessage());
1214
                }
1215
            }
1216

    
1217
            @Override
1218
            protected void onUnauthorized(Response response) {
1219
                sessionExpired();
1220
            }
1221
        };
1222
        copyFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1223
        copyFolder.setHeader(Const.ACCEPT, "*/*");
1224
        copyFolder.setHeader(Const.CONTENT_LENGTH, "0");
1225
        copyFolder.setHeader(Const.CONTENT_TYPE, "application/directory");
1226
        if(!f.getOwnerID().equals(targetUsername)) {
1227
            copyFolder.setHeader(Const.X_SOURCE_ACCOUNT, f.getOwnerID());
1228
        }
1229
        if(move) {
1230
            copyFolder.setHeader(Const.X_MOVE_FROM, URL.encodePathSegment(f.getUri()));
1231
        }
1232
        else {
1233
            copyFolder.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(f.getUri()));
1234
        }
1235
        Scheduler.get().scheduleDeferred(copyFolder);
1236
    }
1237

    
1238
    public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1239
        selectionModels.add(model);
1240
    }
1241

    
1242
    public OtherSharedTreeView getOtherSharedTreeView() {
1243
        return otherSharedTreeView;
1244
    }
1245

    
1246
    public void updateTrash(boolean showFiles, Command callback) {
1247
        updateFolder(trash, showFiles, callback, true);
1248
    }
1249

    
1250
    public void updateGroupsNode() {
1251
        groupTreeView.updateGroupNode(null);
1252
    }
1253

    
1254
    public Group addGroup(String groupname) {
1255
        Group newGroup = new Group(groupname);
1256
        account.addGroup(newGroup);
1257
        groupTreeView.updateGroupNode(null);
1258
        return newGroup;
1259
    }
1260

    
1261
    public void removeGroup(Group group) {
1262
        account.removeGroup(group);
1263
        updateGroupsNode();
1264
    }
1265

    
1266
    public TreeView getSelectedTree() {
1267
        return selectedTree;
1268
    }
1269

    
1270
    public void setSelectedTree(TreeView selected) {
1271
        selectedTree = selected;
1272
    }
1273

    
1274
    public Folder getSelection() {
1275
        if(selectedTree != null) {
1276
            return selectedTree.getSelection();
1277
        }
1278
        return null;
1279
    }
1280

    
1281
    public void showFolderStatistics(int folderFileCount) {
1282
        numOfFiles.setHTML(String.valueOf(folderFileCount));
1283
    }
1284

    
1285
    public GroupTreeView getGroupTreeView() {
1286
        return groupTreeView;
1287
    }
1288

    
1289
    public void sessionExpired() {
1290
        new SessionExpiredDialog(this).center();
1291
    }
1292

    
1293
    public void updateRootFolder(Command callback) {
1294
        updateFolder(account.getPithos(), false, callback, true);
1295
    }
1296

    
1297
    void createMySharedTree() {
1298
        LOG("Pithos::createMySharedTree()");
1299
        mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1300
        mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1301
            @Override
1302
            public void onSelectionChange(SelectionChangeEvent event) {
1303
                if(mysharedTreeSelectionModel.getSelectedObject() != null) {
1304
                    deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1305
                    upload.setEnabled(false);
1306
                    disableUploadArea();
1307
                    updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1308
                    showRelevantToolbarButtons();
1309
                }
1310
                else {
1311
                    if(getSelectedTree().equals(mysharedTreeView)) {
1312
                        setSelectedTree(null);
1313
                    }
1314
                    if(getSelectedTree() == null) {
1315
                        showRelevantToolbarButtons();
1316
                    }
1317
                }
1318
            }
1319
        });
1320
        selectionModels.add(mysharedTreeSelectionModel);
1321
        mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1322
        mysharedTreeViewModel.initialize(new Command() {
1323

    
1324
            @Override
1325
            public void execute() {
1326
                mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1327
                trees.insert(mysharedTreeView, 2);
1328
                treeViews.add(mysharedTreeView);
1329
                createOtherSharedTree();
1330
            }
1331
        });
1332
    }
1333

    
1334
    void createOtherSharedTree() {
1335
        LOG("Pithos::createOtherSharedTree()");
1336
        otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1337
        otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1338
            @Override
1339
            public void onSelectionChange(SelectionChangeEvent event) {
1340
                if(otherSharedTreeSelectionModel.getSelectedObject() != null) {
1341
                    deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1342
                    applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1343
                    updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1344
                    showRelevantToolbarButtons();
1345
                }
1346
                else {
1347
                    if(getSelectedTree().equals(otherSharedTreeView)) {
1348
                        setSelectedTree(null);
1349
                    }
1350
                    if(getSelectedTree() == null) {
1351
                        showRelevantToolbarButtons();
1352
                    }
1353
                }
1354
            }
1355
        });
1356
        selectionModels.add(otherSharedTreeSelectionModel);
1357
        otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1358
        // #3784 We show it empty...
1359
        otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1360
        trees.insert(otherSharedTreeView, 1);
1361

    
1362
        LOG("Pithos::createOtherSharedTree(), initializing otherSharedTreeViewModel with a callback");
1363
        otherSharedTreeViewModel.initialize(new Command() {
1364
            @Override
1365
            public void execute() {
1366
                // #3784 ... then remove the empty stuff and add a new view with the populated model
1367
                trees.remove(otherSharedTreeView);
1368

    
1369
                otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel);
1370
                trees.insert(otherSharedTreeView, 1);
1371
                treeViews.add(otherSharedTreeView);
1372
                scheduleResfresh();
1373
            }
1374
        });
1375
    }
1376

    
1377
    public String getErrorData() {
1378
        final StringBuilder sb = new StringBuilder();
1379
        final String NL = Const.NL;
1380
        Throwable t = this.error;
1381
        while(t != null) {
1382
            sb.append(t.toString());
1383
            sb.append(NL);
1384
            StackTraceElement[] traces = t.getStackTrace();
1385
            for(StackTraceElement trace : traces) {
1386
                sb.append("  [");
1387
                sb.append(trace.getClassName());
1388
                sb.append("::");
1389
                sb.append(trace.getMethodName());
1390
                sb.append("() at ");
1391
                sb.append(trace.getFileName());
1392
                sb.append(":");
1393
                sb.append(trace.getLineNumber());
1394
                sb.append("]");
1395
                sb.append(NL);
1396
            }
1397
            t = t.getCause();
1398
        }
1399

    
1400
        return sb.toString();
1401
    }
1402

    
1403
    public void setError(Throwable t) {
1404
        error = t;
1405
        LOG(t);
1406
    }
1407

    
1408
    public void showRelevantToolbarButtons() {
1409
        toolbar.showRelevantButtons();
1410
    }
1411

    
1412
    public FileUploadDialog getFileUploadDialog() {
1413
        if(fileUploadDialog == null) {
1414
            fileUploadDialog = new FileUploadDialog(this);
1415
        }
1416
        return fileUploadDialog;
1417
    }
1418

    
1419
    public void hideUploadIndicator() {
1420
        upload.removeStyleName("pithos-uploadButton-loading");
1421
        upload.setTitle("");
1422
    }
1423

    
1424
    public void showUploadIndicator() {
1425
        upload.addStyleName("pithos-uploadButton-loading");
1426
        upload.setTitle("Upload in progress. Click for details.");
1427
    }
1428

    
1429
    public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1430
        if(folder == null) {
1431
            if(callback != null) {
1432
                callback.execute();
1433
            }
1434
        }
1435
        else {
1436
            HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getStorageAPIURL(), folder.getOwnerID(), folder.getUri(), folder) {
1437

    
1438
                @Override
1439
                public void onSuccess(Folder _result) {
1440
                    if(callback != null) {
1441
                        callback.execute();
1442
                    }
1443
                }
1444

    
1445
                @Override
1446
                public void onError(Throwable t) {
1447
                    if(t instanceof RestException) {
1448
                        if(((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1449
                            final String path = folder.getUri();
1450
                            PutRequest newFolder = new PutRequest(getStorageAPIURL(), folder.getOwnerID(), path) {
1451
                                @Override
1452
                                public void onSuccess(Resource _result) {
1453
                                    scheduleFolderHeadCommand(folder, callback);
1454
                                }
1455

    
1456
                                @Override
1457
                                public void onError(Throwable _t) {
1458
                                    setError(_t);
1459
                                    if(_t instanceof RestException) {
1460
                                        displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1461
                                    }
1462
                                    else {
1463
                                        displayError("System error creating folder: " + _t.getMessage());
1464
                                    }
1465
                                }
1466

    
1467
                                @Override
1468
                                protected void onUnauthorized(Response response) {
1469
                                    sessionExpired();
1470
                                }
1471
                            };
1472
                            newFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1473
                            newFolder.setHeader(Const.CONTENT_TYPE, "application/folder");
1474
                            newFolder.setHeader(Const.ACCEPT, "*/*");
1475
                            newFolder.setHeader(Const.CONTENT_LENGTH, "0");
1476
                            Scheduler.get().scheduleDeferred(newFolder);
1477
                        }
1478
                        else if(((RestException) t).getHttpStatusCode() == Response.SC_FORBIDDEN) {
1479
                            onSuccess(folder);
1480
                        }
1481
                        else {
1482
                            displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1483
                        }
1484
                    }
1485
                    else {
1486
                        displayError("System error heading folder: " + t.getMessage());
1487
                    }
1488

    
1489
                    LOG("Error heading folder", t);
1490
                    setError(t);
1491
                }
1492

    
1493
                @Override
1494
                protected void onUnauthorized(Response response) {
1495
                    sessionExpired();
1496
                }
1497
            };
1498
            headFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1499
            Scheduler.get().scheduleDeferred(headFolder);
1500
        }
1501
    }
1502

    
1503
    public void scheduleFileHeadCommand(File f, final Command callback) {
1504
        HeadRequest<File> headFile = new HeadRequest<File>(File.class, getStorageAPIURL(), f.getOwnerID(), f.getUri(), f) {
1505

    
1506
            @Override
1507
            public void onSuccess(File _result) {
1508
                if(callback != null) {
1509
                    callback.execute();
1510
                }
1511
            }
1512

    
1513
            @Override
1514
            public void onError(Throwable t) {
1515
                LOG("Error heading file", t);
1516
                setError(t);
1517
                if(t instanceof RestException) {
1518
                    displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1519
                }
1520
                else {
1521
                    displayError("System error heading file: " + t.getMessage());
1522
                }
1523
            }
1524

    
1525
            @Override
1526
            protected void onUnauthorized(Response response) {
1527
                sessionExpired();
1528
            }
1529
        };
1530
        headFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1531
        Scheduler.get().scheduleDeferred(headFile);
1532
    }
1533

    
1534
    public boolean isMySharedSelected() {
1535
        return getSelectedTree().equals(getMySharedTreeView());
1536
    }
1537

    
1538
    private Folder getUploadFolder() {
1539
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1540
            return getSelection();
1541
        }
1542
        return null;
1543
    }
1544

    
1545
    private void updateUploadFolder() {
1546
        updateUploadFolder(null);
1547
    }
1548

    
1549
    private void updateUploadFolder(final JsArrayString urls) {
1550
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1551
            Folder f = getSelection();
1552
            if(getSelectedTree().equals(getFolderTreeView())) {
1553
                updateFolder(f, true, new Command() {
1554

    
1555
                    @Override
1556
                    public void execute() {
1557
                        updateStatistics();
1558
                        if(urls != null) {
1559
                            selectUploadedFiles(urls);
1560
                        }
1561
                    }
1562
                }, false);
1563
            }
1564
            else {
1565
                updateOtherSharedFolder(f, true, null);
1566
            }
1567
        }
1568
    }
1569

    
1570
    public native void disableUploadArea() /*-{
1571
      var uploader = $wnd.$("#uploader").pluploadQueue();
1572
      var dropElm = $wnd.document.getElementById('rightPanel');
1573
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1574
    }-*/;
1575

    
1576
    public native void enableUploadArea() /*-{
1577
      var uploader = $wnd.$("#uploader").pluploadQueue();
1578
      var dropElm = $wnd.document.getElementById('rightPanel');
1579
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1580
      if (uploader.runtime == 'html5') {
1581
        uploader.settings.drop_element = 'rightPanel';
1582
        uploader.trigger('PostInit');
1583
      }
1584
    }-*/;
1585

    
1586
    public void showUploadAlert(int nOfFiles) {
1587
        if(uploadAlert == null) {
1588
            uploadAlert = new UploadAlert(this, nOfFiles);
1589
        }
1590
        if(!uploadAlert.isShowing()) {
1591
            uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1592

    
1593
                @Override
1594
                public void setPosition(int offsetWidth, int offsetHeight) {
1595
                    uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth) / 2, statusPanel.getAbsoluteTop() - offsetHeight);
1596
                }
1597
            });
1598
        }
1599
        uploadAlert.setNumOfFiles(nOfFiles);
1600
    }
1601

    
1602
    public void hideUploadAlert() {
1603
        if(uploadAlert != null && uploadAlert.isShowing()) {
1604
            uploadAlert.hide();
1605
        }
1606
    }
1607

    
1608
    public void selectUploadedFiles(JsArrayString urls) {
1609
        List<String> selectedUrls = new ArrayList<String>();
1610
        for(int i = 0; i < urls.length(); i++) {
1611
            selectedUrls.add(urls.get(i));
1612
        }
1613
        fileList.selectByUrl(selectedUrls);
1614
    }
1615

    
1616
    public void purgeContainer(final Folder container) {
1617
        String path = "/" + container.getName() + "?delimiter=/";
1618
        DeleteRequest delete = new DeleteRequest(getStorageAPIURL(), getUserID(), path) {
1619

    
1620
            @Override
1621
            protected void onUnauthorized(Response response) {
1622
                sessionExpired();
1623
            }
1624

    
1625
            @Override
1626
            public void onSuccess(Resource result) {
1627
                updateFolder(container, true, null, true);
1628
            }
1629

    
1630
            @Override
1631
            public void onError(Throwable t) {
1632
                LOG("Error deleting trash", t);
1633
                setError(t);
1634
                if(t instanceof RestException) {
1635
                    displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1636
                }
1637
                else {
1638
                    displayError("System error deleting trash: " + t.getMessage());
1639
                }
1640
            }
1641
        };
1642
        delete.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1643
        Scheduler.get().scheduleDeferred(delete);
1644
    }
1645
}