Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (58.6 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

    
112
    public static final String OTHERPROPS_STORAGE_API_URL = getFromOtherPropertiesOrNull("STORAGE_API_URL");
113
    public static final String OTHERPROPS_USER_CATALOGS_API_URL = getFromOtherPropertiesOrNull("USER_CATALOGS_API_URL");
114
    static {
115
        LOG("STORAGE_API_URL = ", OTHERPROPS_STORAGE_API_URL);
116
        LOG("USER_CATALOGS_API_URL = ", OTHERPROPS_USER_CATALOGS_API_URL);
117
    }
118

    
119
    public static final String STORAGE_API_URL;
120
    static {
121
        if(OTHERPROPS_STORAGE_API_URL != null) {
122
            STORAGE_API_URL = OTHERPROPS_STORAGE_API_URL;
123
        }
124
        else if(CONFIG_API_PATH != null) {
125
            STORAGE_API_URL = CONFIG_API_PATH;
126
        }
127
        else {
128
            throw new RuntimeException("Unknown STORAGE_API_URL");
129
        }
130

    
131
        LOG("Computed STORAGE_API_URL = ", STORAGE_API_URL);
132
    }
133

    
134
    public static final String STORAGE_VIEW_URL;
135
    static {
136
        final String viewURL = getFromOtherPropertiesOrNull("STORAGE_VIEW_URL");
137
        if(viewURL != null) {
138
            STORAGE_VIEW_URL = viewURL;
139
        }
140
        else {
141
            STORAGE_VIEW_URL = STORAGE_API_URL;
142
        }
143

    
144
        LOG("Computed STORAGE_VIEW_URL = ", STORAGE_VIEW_URL);
145
    }
146

    
147
    public static final String USER_CATALOGS_API_URL;
148
    static {
149
        if(OTHERPROPS_USER_CATALOGS_API_URL != null) {
150
            USER_CATALOGS_API_URL = OTHERPROPS_USER_CATALOGS_API_URL;
151
        }
152
        else if(OTHERPROPS_STORAGE_API_URL != null) {
153
            throw new RuntimeException("STORAGE_API_URL is defined but USER_CATALOGS_API_URL is not");
154
        }
155
        else {
156
            // https://server.com/v1/ --> https://server.com
157
            String url = CONFIG_API_PATH;
158
            url = Helpers.stripTrailing(url, "/");
159
            url = Helpers.upToIncludingLastPart(url, "/");
160
            url = Helpers.stripTrailing(url, "/");
161
            url = url + "/user_catalogs";
162

    
163
            USER_CATALOGS_API_URL = url;
164

    
165
            LOG("Computed USER_CATALOGS_API_URL = ", USER_CATALOGS_API_URL);
166
        }
167
    }
168

    
169
    public interface Style extends CssResource {
170
        String commandAnchor();
171

    
172
        String statistics();
173

    
174
        @ClassName("gwt-HTML")
175
        String html();
176

    
177
        String uploadAlert();
178

    
179
        String uploadAlertLink();
180

    
181
        String uploadAlertProgress();
182

    
183
        String uploadAlertPercent();
184

    
185
        String uploadAlertClose();
186
    }
187

    
188
    public interface Resources extends ClientBundle {
189
        @Source("Pithos.css")
190
        Style pithosCss();
191

    
192
        @Source("gr/grnet/pithos/resources/close-popup.png")
193
        ImageResource closePopup();
194
    }
195

    
196
    public static Resources resources = GWT.create(Resources.class);
197

    
198
    /**
199
     * Instantiate an application-level image bundle. This object will provide
200
     * programmatic access to all the images needed by widgets.
201
     */
202
    static Images images = (Images) GWT.create(Images.class);
203

    
204
    public String getUserID() {
205
        return userID;
206
    }
207

    
208
    public UserCatalogs getUserCatalogs() {
209
        return userCatalogs;
210
    }
211

    
212
    public String getCurrentUserDisplayNameOrID() {
213
        final String displayName = userCatalogs.getDisplayName(getUserID());
214
        return displayName == null ? getUserID() : displayName;
215
    }
216

    
217
    public boolean hasDisplayNameForUserID(String userID) {
218
        return userCatalogs.getDisplayName(userID) != null;
219
    }
220

    
221
    public boolean hasIDForUserDisplayName(String userDisplayName) {
222
        return userCatalogs.getID(userDisplayName) != null;
223
    }
224

    
225
    public String getDisplayNameForUserID(String userID) {
226
        return userCatalogs.getDisplayName(userID);
227
    }
228

    
229
    public String getIDForUserDisplayName(String userDisplayName) {
230
        return userCatalogs.getID(userDisplayName);
231
    }
232

    
233
    public List<String> getDisplayNamesForUserIDs(List<String> userIDs) {
234
        if(userIDs == null) {
235
            userIDs = new ArrayList<String>();
236
        }
237
        final List<String> userDisplayNames = new ArrayList<String>();
238
        for(String userID : userIDs) {
239
            final String displayName = getDisplayNameForUserID(userID);
240
            userDisplayNames.add(displayName);
241
        }
242

    
243
        return userDisplayNames;
244
    }
245

    
246
    public List<String> filterUserIDsWithUnknownDisplayName(Collection<String> userIDs) {
247
        if(userIDs == null) {
248
            userIDs = new ArrayList<String>();
249
        }
250
        final List<String> filtered = new ArrayList<String>();
251
        for(String userID : userIDs) {
252
            if(!this.userCatalogs.hasID(userID)) {
253
                filtered.add(userID);
254
            }
255
        }
256
        return filtered;
257
    }
258

    
259
    public void setAccount(AccountResource acct) {
260
        account = acct;
261
    }
262

    
263
    public AccountResource getAccount() {
264
        return account;
265
    }
266

    
267
    public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
268
        folderTreeView.updateFolder(f, showfiles, callback, openParent);
269
    }
270

    
271
    public void updateGroupNode(Group group) {
272
        groupTreeView.updateGroupNode(group);
273
    }
274

    
275
    public void updateMySharedRoot() {
276
        mysharedTreeView.updateRoot();
277
    }
278

    
279
    public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
280
        mysharedTreeView.updateFolder(f, showfiles, callback);
281
    }
282

    
283
    public void updateSharedFolder(Folder f, boolean showfiles) {
284
        updateSharedFolder(f, showfiles, null);
285
    }
286

    
287
    public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
288
        otherSharedTreeView.updateFolder(f, showfiles, callback);
289
    }
290

    
291
    public MysharedTreeView getMySharedTreeView() {
292
        return mysharedTreeView;
293
    }
294

    
295
    /**
296
     * An aggregate image bundle that pulls together all the images for this
297
     * application into a single bundle.
298
     */
299
    public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
300

    
301
        @Source("gr/grnet/pithos/resources/document.png")
302
        ImageResource folders();
303

    
304
        @Source("gr/grnet/pithos/resources/advancedsettings.png")
305
        @ImageOptions(width = 32, height = 32)
306
        ImageResource tools();
307
    }
308

    
309
    private Throwable error;
310

    
311
    /**
312
     * The Application Clipboard implementation;
313
     */
314
    private Clipboard clipboard = new Clipboard();
315

    
316
    /**
317
     * The top panel that contains the menu bar.
318
     */
319
    private TopPanel topPanel;
320

    
321
    /**
322
     * The panel that contains the various system messages.
323
     */
324
    private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
325

    
326
    /**
327
     * The bottom panel that contains the status bar.
328
     */
329
    StatusPanel statusPanel = null;
330

    
331
    /**
332
     * The file list widget.
333
     */
334
    private FileList fileList;
335

    
336
    /**
337
     * The tab panel that occupies the right side of the screen.
338
     */
339
    private VerticalPanel inner = new VerticalPanel();
340

    
341

    
342
    /**
343
     * The split panel that will contain the left and right panels.
344
     */
345
    private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
346

    
347
    /**
348
     * The currently selected item in the application, for use by the Edit menu
349
     * commands. Potential types are Folder, File, User and Group.
350
     */
351
    private Object currentSelection;
352

    
353
    public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
354

    
355
    /**
356
     * The ID that uniquely identifies the user in Pithos+.
357
     * Currently this is a UUID. It used to be the user's email.
358
     */
359
    private String userID = null;
360

    
361
    /**
362
     * Holds mappings from user UUIDs to emails and vice-versa.
363
     */
364
    private UserCatalogs userCatalogs = new UserCatalogs();
365

    
366
    /**
367
     * The authentication token of the current user.
368
     */
369
    private String userToken;
370

    
371
    VerticalPanel trees;
372

    
373
    SingleSelectionModel<Folder> folderTreeSelectionModel;
374
    FolderTreeViewModel folderTreeViewModel;
375
    FolderTreeView folderTreeView;
376

    
377
    SingleSelectionModel<Folder> mysharedTreeSelectionModel;
378
    MysharedTreeViewModel mysharedTreeViewModel;
379
    MysharedTreeView mysharedTreeView = null;
380

    
381
    protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
382
    OtherSharedTreeViewModel otherSharedTreeViewModel;
383
    OtherSharedTreeView otherSharedTreeView = null;
384

    
385
    GroupTreeViewModel groupTreeViewModel;
386
    GroupTreeView groupTreeView;
387

    
388
    TreeView selectedTree;
389
    protected AccountResource account;
390

    
391
    Folder trash;
392

    
393
    List<Composite> treeViews = new ArrayList<Composite>();
394

    
395
    @SuppressWarnings("rawtypes")
396
    List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
397

    
398
    public Button upload;
399

    
400
    private HTML numOfFiles;
401

    
402
    private Toolbar toolbar;
403

    
404
    private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
405

    
406
    UploadAlert uploadAlert;
407

    
408
    Date lastModified;
409

    
410
    @Override
411
    public void onModuleLoad() {
412
        if(parseUserCredentials()) {
413
            initialize();
414
        }
415
    }
416

    
417
    static native void __ConsoleLog(String message) /*-{
418
      try { console.log(message); } catch (e) {}
419
    }-*/;
420

    
421
    public static void LOGError(Throwable error, StringBuilder sb) {
422
        if(!isLOGEnabled()) { return; }
423

    
424
        sb.append("\nException: [" + error.toString().replace("\n", "\n  ") + "]");
425
        Throwable cause = error.getCause();
426
        if(cause != null) {
427
            sb.append("\nCauses:\n");
428
            while(cause != null) {
429
                sb.append("  ");
430
                sb.append("[" + cause.toString().replace("\n", "\n  ")  + "]");
431
                sb.append("\n");
432
                cause = cause.getCause();
433
            }
434
        }
435
        else {
436
            sb.append("\n");
437
        }
438

    
439
        StackTraceElement[] stackTrace = error.getStackTrace();
440
        sb.append("Stack trace (" + stackTrace.length + " elements):\n");
441
        for(int i = 0; i < stackTrace.length; i++) {
442
            StackTraceElement errorElem = stackTrace[i];
443
            sb.append("  [" + i + "] ");
444
            sb.append(errorElem.toString());
445
            sb.append("\n");
446
        }
447
    }
448

    
449
    public static void LOGError(Throwable error) {
450
        if(!isLOGEnabled()) { return; }
451

    
452
        final StringBuilder sb = new StringBuilder();
453
        LOGError(error, sb);
454
        if(sb.length() > 0) {
455
            __ConsoleLog(sb.toString());
456
        }
457
    }
458

    
459
    public static boolean isLOGEnabled() {
460
        return IsLOGEnabled;
461
    }
462

    
463
    public static void LOG(Object ...args) {
464
        if(!isLOGEnabled()) { return; }
465

    
466
        final StringBuilder sb = new StringBuilder();
467
        for(Object arg : args) {
468
            if(arg instanceof Throwable) {
469
                LOGError((Throwable) arg, sb);
470
            }
471
            else {
472
                sb.append(arg);
473
            }
474
        }
475

    
476
        if(sb.length() > 0) {
477
            __ConsoleLog(sb.toString());
478
        }
479
    }
480

    
481
    private void initialize() {
482
        userCatalogs.updateWithIDAndName("*", "All Pithos users");
483

    
484
        lastModified = new Date(); //Initialize if-modified-since value with now.
485
        resources.pithosCss().ensureInjected();
486
        boolean bareContent = Window.Location.getParameter("noframe") != null;
487
        String contentWidth = bareContent ? Const.PERCENT_100 : Const.PERCENT_75;
488

    
489
        VerticalPanel outer = new VerticalPanel();
490
        outer.setWidth(Const.PERCENT_100);
491
        if(!bareContent) {
492
            outer.addStyleName("pithos-outer");
493
        }
494

    
495
        if(!bareContent) {
496
            topPanel = new TopPanel(this, Pithos.images);
497
            topPanel.setWidth(Const.PERCENT_100);
498
            outer.add(topPanel);
499
            outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
500
        }
501

    
502
        messagePanel.setVisible(false);
503
        outer.add(messagePanel);
504
        outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
505
        outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
506

    
507
        HorizontalPanel header = new HorizontalPanel();
508
        header.addStyleName("pithos-header");
509
        header.setWidth(contentWidth);
510
        if(bareContent) {
511
            header.addStyleName("pithos-header-noframe");
512
        }
513
        upload = new Button("Upload", new ClickHandler() {
514
            @Override
515
            public void onClick(ClickEvent event) {
516
                if(getSelection() != null) {
517
                    new UploadFileCommand(Pithos.this, null, getSelection()).execute();
518
                }
519
            }
520
        });
521
        upload.addStyleName("pithos-uploadButton");
522
        header.add(upload);
523
        header.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_LEFT);
524
        header.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
525

    
526
        toolbar = new Toolbar(this);
527
        header.add(toolbar);
528
        header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
529
        header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
530

    
531
        HorizontalPanel folderStatistics = new HorizontalPanel();
532
        folderStatistics.addStyleName("pithos-folderStatistics");
533
        numOfFiles = new HTML();
534
        folderStatistics.add(numOfFiles);
535
        folderStatistics.setCellVerticalAlignment(numOfFiles, HasVerticalAlignment.ALIGN_MIDDLE);
536
        HTML numOfFilesLabel = new HTML("&nbsp;Files");
537
        folderStatistics.add(numOfFilesLabel);
538
        folderStatistics.setCellVerticalAlignment(numOfFilesLabel, HasVerticalAlignment.ALIGN_MIDDLE);
539
        header.add(folderStatistics);
540
        header.setCellHorizontalAlignment(folderStatistics, HasHorizontalAlignment.ALIGN_RIGHT);
541
        header.setCellVerticalAlignment(folderStatistics, HasVerticalAlignment.ALIGN_MIDDLE);
542
        header.setCellWidth(folderStatistics, "40px");
543
        outer.add(header);
544
        outer.setCellHorizontalAlignment(header, HasHorizontalAlignment.ALIGN_CENTER);
545
        // Inner contains the various lists
546
        inner.sinkEvents(Event.ONCONTEXTMENU);
547
        inner.setWidth(Const.PERCENT_100);
548

    
549
        folderTreeSelectionModel = new SingleSelectionModel<Folder>();
550
        folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
551
            @Override
552
            public void onSelectionChange(SelectionChangeEvent event) {
553
                if(folderTreeSelectionModel.getSelectedObject() != null) {
554
                    deselectOthers(folderTreeView, folderTreeSelectionModel);
555
                    applyPermissions(folderTreeSelectionModel.getSelectedObject());
556
                    Folder f = folderTreeSelectionModel.getSelectedObject();
557
                    updateFolder(f, true, new Command() {
558

    
559
                        @Override
560
                        public void execute() {
561
                            updateStatistics();
562
                        }
563
                    }, true);
564
                    showRelevantToolbarButtons();
565
                }
566
                else {
567
                    if(getSelectedTree().equals(folderTreeView)) {
568
                        setSelectedTree(null);
569
                    }
570
                    if(getSelectedTree() == null) {
571
                        showRelevantToolbarButtons();
572
                    }
573
                }
574
            }
575
        });
576
        selectionModels.add(folderTreeSelectionModel);
577

    
578
        folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
579
        folderTreeView = new FolderTreeView(folderTreeViewModel);
580
        treeViews.add(folderTreeView);
581

    
582
        fileList = new FileList(this, images);
583
        inner.add(fileList);
584

    
585
        trees = new VerticalPanel();
586
        trees.setWidth(Const.PERCENT_100);
587

    
588
        // Add the left and right panels to the split panel.
589
        splitPanel.setLeftWidget(trees);
590
        FlowPanel right = new FlowPanel();
591
        right.getElement().setId("rightPanel");
592
        right.add(inner);
593
        splitPanel.setRightWidget(right);
594
        splitPanel.setSplitPosition("219px");
595
        splitPanel.setSize(Const.PERCENT_100, Const.PERCENT_100);
596
        splitPanel.addStyleName("pithos-splitPanel");
597
        splitPanel.setWidth(contentWidth);
598
        outer.add(splitPanel);
599
        outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
600

    
601
        if(!bareContent) {
602
            statusPanel = new StatusPanel();
603
            statusPanel.setWidth(Const.PERCENT_100);
604
            outer.add(statusPanel);
605
            outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
606
        }
607
        else {
608
            splitPanel.addStyleName("pithos-splitPanel-noframe");
609
        }
610

    
611
        // Hook the window resize event, so that we can adjust the UI.
612
        Window.addResizeHandler(this);
613
        // Clear out the window's built-in margin, because we want to take
614
        // advantage of the entire client area.
615
        Window.setMargin("0px");
616
        // Finally, add the outer panel to the RootPanel, so that it will be
617
        // displayed.
618
        RootPanel.get().add(outer);
619
        // Call the window resized handler to get the initial sizes setup. Doing
620
        // this in a deferred command causes it to occur after all widgets'
621
        // sizes have been computed by the browser.
622
        Scheduler.get().scheduleIncremental(new RepeatingCommand() {
623

    
624
            @Override
625
            public boolean execute() {
626
                if(!isCloudbarReady()) {
627
                    return true;
628
                }
629
                onWindowResized(Window.getClientHeight());
630
                return false;
631
            }
632
        });
633

    
634
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
635
            @Override
636
            public void execute() {
637
                LOG("Pithos::initialize() Calling Pithos::fetchAccount()");
638
                fetchAccount(new Command() {
639

    
640
                    @Override
641
                    public void execute() {
642
                        if(!account.hasHomeContainer()) {
643
                            createHomeContainer(account, this);
644
                        }
645
                        else if(!account.hasTrashContainer()) {
646
                            createTrashContainer(this);
647
                        }
648
                        else {
649
                            for(Folder f : account.getContainers()) {
650
                                if(f.getName().equals(Const.TRASH_CONTAINER)) {
651
                                    trash = f;
652
                                    break;
653
                                }
654
                            }
655
                            trees.add(folderTreeView);
656
                            folderTreeViewModel.initialize(account, new Command() {
657

    
658
                                @Override
659
                                public void execute() {
660
                                    createMySharedTree();
661
                                }
662
                            });
663

    
664
                            HorizontalPanel separator = new HorizontalPanel();
665
                            separator.addStyleName("pithos-statisticsSeparator");
666
                            separator.add(new HTML(""));
667
                            trees.add(separator);
668

    
669
                            groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
670
                            groupTreeView = new GroupTreeView(groupTreeViewModel);
671
                            treeViews.add(groupTreeView);
672
                            trees.add(groupTreeView);
673
                            folderTreeView.showStatistics(account);
674
                        }
675
                    }
676
                });
677
            }
678
        });
679
    }
680

    
681
    public void scheduleResfresh() {
682
        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
683

    
684
            @Override
685
            public boolean execute() {
686
                final Folder f = getSelection();
687
                if(f == null) {
688
                    return true;
689
                }
690

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

    
693
                    @Override
694
                    public void onSuccess(Folder _result) {
695
                        lastModified = new Date();
696
                        if(getSelectedTree().equals(folderTreeView)) {
697
                            updateFolder(f, true, new Command() {
698

    
699
                                @Override
700
                                public void execute() {
701
                                    scheduleResfresh();
702
                                }
703

    
704
                            }, false);
705
                        }
706
                        else if(getSelectedTree().equals(mysharedTreeView)) {
707
                            updateSharedFolder(f, true, new Command() {
708

    
709
                                @Override
710
                                public void execute() {
711
                                    scheduleResfresh();
712
                                }
713
                            });
714
                        }
715
                        else {
716
                            scheduleResfresh();
717
                        }
718
                    }
719

    
720
                    @Override
721
                    public void onError(Throwable t) {
722
                        if(t instanceof RestException && ((RestException) t).getHttpStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
723
                            scheduleResfresh();
724
                        }
725
                        else if(retries >= MAX_RETRIES) {
726
                            LOG("Error heading folder. ", t);
727
                            setError(t);
728
                            if(t instanceof RestException) {
729
                                displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
730
                            }
731
                            else {
732
                                displayError("System error heading folder: " + t.getMessage());
733
                            }
734
                        }
735
                        else {//retry
736
                            LOG("Retry ", retries);
737
                            Scheduler.get().scheduleDeferred(this);
738
                        }
739
                    }
740

    
741
                    @Override
742
                    protected void onUnauthorized(Response response) {
743
                        if(retries >= MAX_RETRIES) {
744
                            sessionExpired();
745
                        }
746
                        else //retry
747
                        {
748
                            Scheduler.get().scheduleDeferred(this);
749
                        }
750
                    }
751
                };
752
                head.setHeader(Const.X_AUTH_TOKEN, getUserToken());
753
                head.setHeader(Const.IF_MODIFIED_SINCE, DateTimeFormat.getFormat(Const.DATE_FORMAT_1).format(lastModified, TimeZone.createTimeZone(0)) + " GMT");
754
                Scheduler.get().scheduleDeferred(head);
755

    
756
                return false;
757
            }
758
        }, 3000);
759
    }
760

    
761
    public void applyPermissions(Folder f) {
762
        if(f != null) {
763
            if(f.isInTrash()) {
764
                upload.setEnabled(false);
765
                disableUploadArea();
766
            }
767
            else {
768
                Boolean[] perms = f.getPermissions().get(userID);
769
                if(f.getOwnerID().equals(userID) || (perms != null && perms[1] != null && perms[1])) {
770
                    upload.setEnabled(true);
771
                    enableUploadArea();
772
                }
773
                else {
774
                    upload.setEnabled(false);
775
                    disableUploadArea();
776
                }
777
            }
778
        }
779
        else {
780
            upload.setEnabled(false);
781
            disableUploadArea();
782
        }
783
    }
784

    
785
    @SuppressWarnings({"rawtypes", "unchecked"})
786
    public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
787
        selectedTree = _selectedTree;
788

    
789
        for(SingleSelectionModel s : selectionModels) {
790
            if(!s.equals(model) && s.getSelectedObject() != null) {
791
                s.setSelected(s.getSelectedObject(), false);
792
            }
793
        }
794
    }
795

    
796
    public void showFiles(final Folder f) {
797
        Set<File> files = f.getFiles();
798
        showFiles(files);
799
    }
800

    
801
    public void showFiles(Set<File> files) {
802
        fileList.setFiles(new ArrayList<File>(files));
803
    }
804

    
805
    /**
806
     * Parse and store the user credentials to the appropriate fields.
807
     */
808
    private boolean parseUserCredentials() {
809
        final String cookie = otherProperties.get(Const.AUTH_COOKIE);
810
        String auth = Cookies.getCookie(cookie);
811
        if(auth == null) {
812
            authenticateUser();
813
            return false;
814
        }
815
        if(auth.startsWith("\"")) {
816
            auth = auth.substring(1);
817
        }
818
        if(auth.endsWith("\"")) {
819
            auth = auth.substring(0, auth.length() - 1);
820
        }
821
        String[] authSplit = auth.split("\\" + config.cookieSeparator(), 2);
822
        if(authSplit.length != 2) {
823
            authenticateUser();
824
            return false;
825
        }
826
        this.userID = authSplit[0];
827
        this.userToken = authSplit[1];
828

    
829
        String gotoUrl = Window.Location.getParameter("goto");
830
        if(gotoUrl != null && gotoUrl.length() > 0) {
831
            Window.Location.assign(gotoUrl);
832
            return false;
833
        }
834
        return true;
835
    }
836

    
837
    /**
838
     * Redirect the user to the login page for authentication.
839
     */
840
    protected void authenticateUser() {
841
        Dictionary otherProperties = Dictionary.getDictionary(Const.OTHER_PROPERTIES);
842
        Window.Location.assign(otherProperties.get(Const.LOGIN_URL) + Window.Location.getHref());
843
    }
844

    
845
    public void fetchAccount(final Command callback) {
846
        String path = "?format=json";
847

    
848
        GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getStorageAPIURL(), userID, path) {
849
            @Override
850
            public void onSuccess(AccountResource accountResource) {
851
                account = accountResource;
852
                if(callback != null) {
853
                    callback.execute();
854
                }
855

    
856
                final List<String> memberIDs = new ArrayList<String>();
857
                final List<Group> groups = account.getGroups();
858
                for(Group group : groups) {
859
                    memberIDs.addAll(group.getMemberIDs());
860
                }
861
                memberIDs.add(Pithos.this.getUserID());
862

    
863
                final List<String> theUnknown = Pithos.this.filterUserIDsWithUnknownDisplayName(memberIDs);
864
                // Initialize the user catalog
865
                new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();
866
                LOG("Called new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();");
867
            }
868

    
869
            @Override
870
            public void onError(Throwable t) {
871
                LOG("Error getting account", t);
872
                setError(t);
873
                if(t instanceof RestException) {
874
                    displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
875
                }
876
                else {
877
                    displayError("System error fetching user data: " + t.getMessage());
878
                }
879
            }
880

    
881
            @Override
882
            protected void onUnauthorized(Response response) {
883
                sessionExpired();
884
            }
885
        };
886
        getAccount.setHeader(Const.X_AUTH_TOKEN, userToken);
887
        Scheduler.get().scheduleDeferred(getAccount);
888
    }
889

    
890
    public void updateStatistics() {
891
        HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getStorageAPIURL(), userID, "", account) {
892

    
893
            @Override
894
            public void onSuccess(AccountResource _result) {
895
                folderTreeView.showStatistics(account);
896
            }
897

    
898
            @Override
899
            public void onError(Throwable t) {
900
                LOG("Error getting account", t);
901
                setError(t);
902
                if(t instanceof RestException) {
903
                    displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
904
                }
905
                else {
906
                    displayError("System error fetching user data: " + t.getMessage());
907
                }
908
            }
909

    
910
            @Override
911
            protected void onUnauthorized(Response response) {
912
                sessionExpired();
913
            }
914
        };
915
        headAccount.setHeader(Const.X_AUTH_TOKEN, userToken);
916
        Scheduler.get().scheduleDeferred(headAccount);
917
    }
918

    
919
    protected void createHomeContainer(final AccountResource _account, final Command callback) {
920
        String path = "/" + Const.HOME_CONTAINER;
921
        PutRequest createPithos = new PutRequest(getStorageAPIURL(), getUserID(), path) {
922
            @Override
923
            public void onSuccess(Resource result) {
924
                if(!_account.hasTrashContainer()) {
925
                    createTrashContainer(callback);
926
                }
927
                else {
928
                    fetchAccount(callback);
929
                }
930
            }
931

    
932
            @Override
933
            public void onError(Throwable t) {
934
                LOG("Error creating pithos", t);
935
                setError(t);
936
                if(t instanceof RestException) {
937
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
938
                }
939
                else {
940
                    displayError("System error Error creating pithos: " + t.getMessage());
941
                }
942
            }
943

    
944
            @Override
945
            protected void onUnauthorized(Response response) {
946
                sessionExpired();
947
            }
948
        };
949
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
950
        Scheduler.get().scheduleDeferred(createPithos);
951
    }
952

    
953
    protected void createTrashContainer(final Command callback) {
954
        String path = "/" + Const.TRASH_CONTAINER;
955
        PutRequest createPithos = new PutRequest(getStorageAPIURL(), getUserID(), path) {
956
            @Override
957
            public void onSuccess(Resource result) {
958
                fetchAccount(callback);
959
            }
960

    
961
            @Override
962
            public void onError(Throwable t) {
963
                LOG("Error creating pithos", t);
964
                setError(t);
965
                if(t instanceof RestException) {
966
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
967
                }
968
                else {
969
                    displayError("System error Error creating pithos: " + t.getMessage());
970
                }
971
            }
972

    
973
            @Override
974
            protected void onUnauthorized(Response response) {
975
                sessionExpired();
976
            }
977
        };
978
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
979
        Scheduler.get().scheduleDeferred(createPithos);
980
    }
981

    
982
    /**
983
     * Creates an HTML fragment that places an image & caption together, for use
984
     * in a group header.
985
     *
986
     * @param imageProto an image prototype for an image
987
     * @param caption    the group caption
988
     * @return the header HTML fragment
989
     */
990
    private String createHeaderHTML(AbstractImagePrototype imageProto, String caption) {
991
        String captionHTML = "<table class='caption' cellpadding='0' "
992
            + "cellspacing='0'>" + "<tr><td class='lcaption'>" + imageProto.getHTML()
993
            + "</td><td id =" + caption + " class='rcaption'><b style='white-space:nowrap'>&nbsp;"
994
            + caption + "</b></td></tr></table>";
995
        return captionHTML;
996
    }
997

    
998
    protected void onWindowResized(int height) {
999
        // Adjust the split panel to take up the available room in the window.
1000
        int newHeight = height - splitPanel.getAbsoluteTop() - 153;
1001
        if(newHeight < 1) {
1002
            newHeight = 1;
1003
        }
1004
        splitPanel.setHeight("" + newHeight);
1005
        inner.setHeight("" + newHeight);
1006
    }
1007

    
1008
    native boolean isCloudbarReady()/*-{
1009
      if ($wnd.$("div.cloudbar") && $wnd.$("div.cloudbar").height() > 0)
1010
        return true;
1011
      return false;
1012
    }-*/;
1013

    
1014
    @Override
1015
    public void onResize(ResizeEvent event) {
1016
        int height = event.getHeight();
1017
        onWindowResized(height);
1018
    }
1019

    
1020
    /**
1021
     * Display an error message.
1022
     *
1023
     * @param msg the message to display
1024
     */
1025
    public void displayError(String msg) {
1026
        messagePanel.displayError(msg);
1027
        onWindowResized(Window.getClientHeight());
1028
    }
1029

    
1030
    /**
1031
     * Display a warning message.
1032
     *
1033
     * @param msg the message to display
1034
     */
1035
    public void displayWarning(String msg) {
1036
        messagePanel.displayWarning(msg);
1037
        onWindowResized(Window.getClientHeight());
1038
    }
1039

    
1040
    /**
1041
     * Display an informational message.
1042
     *
1043
     * @param msg the message to display
1044
     */
1045
    public void displayInformation(String msg) {
1046
        messagePanel.displayInformation(msg);
1047
        onWindowResized(Window.getClientHeight());
1048
    }
1049

    
1050
    /**
1051
     * Retrieve the fileList.
1052
     *
1053
     * @return the fileList
1054
     */
1055
    public FileList getFileList() {
1056
        return fileList;
1057
    }
1058

    
1059
    /**
1060
     * Retrieve the topPanel.
1061
     *
1062
     * @return the topPanel
1063
     */
1064
    TopPanel getTopPanel() {
1065
        return topPanel;
1066
    }
1067

    
1068
    /**
1069
     * Retrieve the clipboard.
1070
     *
1071
     * @return the clipboard
1072
     */
1073
    public Clipboard getClipboard() {
1074
        return clipboard;
1075
    }
1076

    
1077
    public StatusPanel getStatusPanel() {
1078
        return statusPanel;
1079
    }
1080

    
1081
    public String getUserToken() {
1082
        return userToken;
1083
    }
1084

    
1085
    public static native void preventIESelection() /*-{
1086
      $doc.body.onselectstart = function () {
1087
        return false;
1088
      };
1089
    }-*/;
1090

    
1091
    public static native void enableIESelection() /*-{
1092
      if ($doc.body.onselectstart != null)
1093
        $doc.body.onselectstart = null;
1094
    }-*/;
1095

    
1096
    public static String getStorageAPIURL() {
1097
        return STORAGE_API_URL;
1098
    }
1099

    
1100
    public static String getStorageViewURL() {
1101
        return STORAGE_VIEW_URL;
1102
    }
1103

    
1104
    public static String getUserCatalogsURL() {
1105
        return USER_CATALOGS_API_URL;
1106
    }
1107

    
1108
    /**
1109
     * History support for folder navigation
1110
     * adds a new browser history entry
1111
     *
1112
     * @param key
1113
     */
1114
    public void updateHistory(String key) {
1115
//                Replace any whitespace of the initial string to "+"
1116
//                String result = key.replaceAll("\\s","+");
1117
//                Add a new browser history entry.
1118
//                History.newItem(result);
1119
        History.newItem(key);
1120
    }
1121

    
1122
    public void deleteFolder(final Folder folder, final Command callback) {
1123
        final PleaseWaitPopup pwp = new PleaseWaitPopup();
1124
        pwp.center();
1125
        String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
1126
        DeleteRequest deleteFolder = new DeleteRequest(getStorageAPIURL(), folder.getOwnerID(), path) {
1127

    
1128
            @Override
1129
            protected void onUnauthorized(Response response) {
1130
                pwp.hide();
1131
                sessionExpired();
1132
            }
1133

    
1134
            @Override
1135
            public void onSuccess(Resource result) {
1136
                updateFolder(folder.getParent(), true, new Command() {
1137

    
1138
                    @Override
1139
                    public void execute() {
1140
                        folderTreeSelectionModel.setSelected(folder.getParent(), true);
1141
                        updateStatistics();
1142
                        if(callback != null) {
1143
                            callback.execute();
1144
                        }
1145
                        pwp.hide();
1146
                    }
1147
                }, true);
1148
            }
1149

    
1150
            @Override
1151
            public void onError(Throwable t) {
1152
                LOG(t);
1153
                setError(t);
1154
                if(t instanceof RestException) {
1155
                    if(((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND) {
1156
                        displayError("Unable to delete folder: " + ((RestException) t).getHttpStatusText());
1157
                    }
1158
                    else {
1159
                        onSuccess(null);
1160
                    }
1161
                }
1162
                else {
1163
                    displayError("System error unable to delete folder: " + t.getMessage());
1164
                }
1165
                pwp.hide();
1166
            }
1167
        };
1168
        deleteFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1169
        Scheduler.get().scheduleDeferred(deleteFolder);
1170
    }
1171

    
1172
    public FolderTreeView getFolderTreeView() {
1173
        return folderTreeView;
1174
    }
1175

    
1176
    public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
1177
        if(iter.hasNext()) {
1178
            File file = iter.next();
1179
            String path = targetUri + "/" + file.getName();
1180
            PutRequest copyFile = new PutRequest(getStorageAPIURL(), targetUsername, path) {
1181
                @Override
1182
                public void onSuccess(Resource result) {
1183
                    copyFiles(iter, targetUsername, targetUri, callback);
1184
                }
1185

    
1186
                @Override
1187
                public void onError(Throwable t) {
1188
                    LOG(t);
1189
                    setError(t);
1190
                    if(t instanceof RestException) {
1191
                        displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
1192
                    }
1193
                    else {
1194
                        displayError("System error unable to copy file: " + t.getMessage());
1195
                    }
1196
                }
1197

    
1198
                @Override
1199
                protected void onUnauthorized(Response response) {
1200
                    sessionExpired();
1201
                }
1202
            };
1203
            copyFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1204
            copyFile.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(file.getUri()));
1205
            if(!file.getOwnerID().equals(targetUsername)) {
1206
                copyFile.setHeader(Const.X_SOURCE_ACCOUNT, URL.encodePathSegment(file.getOwnerID()));
1207
            }
1208
            copyFile.setHeader(Const.CONTENT_TYPE, file.getContentType());
1209
            Scheduler.get().scheduleDeferred(copyFile);
1210
        }
1211
        else if(callback != null) {
1212
            callback.execute();
1213
        }
1214
    }
1215

    
1216
    public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
1217
        String path = targetUri + "?delimiter=/";
1218
        PutRequest copyFolder = new PutRequest(getStorageAPIURL(), targetUsername, path) {
1219
            @Override
1220
            public void onSuccess(Resource result) {
1221
                if(callback != null) {
1222
                    callback.execute();
1223
                }
1224
            }
1225

    
1226
            @Override
1227
            public void onError(Throwable t) {
1228
                LOG(t);
1229
                setError(t);
1230
                if(t instanceof RestException) {
1231
                    displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1232
                }
1233
                else {
1234
                    displayError("System error copying folder: " + t.getMessage());
1235
                }
1236
            }
1237

    
1238
            @Override
1239
            protected void onUnauthorized(Response response) {
1240
                sessionExpired();
1241
            }
1242
        };
1243
        copyFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1244
        copyFolder.setHeader(Const.ACCEPT, "*/*");
1245
        copyFolder.setHeader(Const.CONTENT_LENGTH, "0");
1246
        copyFolder.setHeader(Const.CONTENT_TYPE, "application/directory");
1247
        if(!f.getOwnerID().equals(targetUsername)) {
1248
            copyFolder.setHeader(Const.X_SOURCE_ACCOUNT, f.getOwnerID());
1249
        }
1250
        if(move) {
1251
            copyFolder.setHeader(Const.X_MOVE_FROM, URL.encodePathSegment(f.getUri()));
1252
        }
1253
        else {
1254
            copyFolder.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(f.getUri()));
1255
        }
1256
        Scheduler.get().scheduleDeferred(copyFolder);
1257
    }
1258

    
1259
    public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1260
        selectionModels.add(model);
1261
    }
1262

    
1263
    public OtherSharedTreeView getOtherSharedTreeView() {
1264
        return otherSharedTreeView;
1265
    }
1266

    
1267
    public void updateTrash(boolean showFiles, Command callback) {
1268
        updateFolder(trash, showFiles, callback, true);
1269
    }
1270

    
1271
    public void updateGroupsNode() {
1272
        groupTreeView.updateGroupNode(null);
1273
    }
1274

    
1275
    public Group addGroup(String groupname) {
1276
        Group newGroup = new Group(groupname);
1277
        account.addGroup(newGroup);
1278
        groupTreeView.updateGroupNode(null);
1279
        return newGroup;
1280
    }
1281

    
1282
    public void removeGroup(Group group) {
1283
        account.removeGroup(group);
1284
        updateGroupsNode();
1285
    }
1286

    
1287
    public TreeView getSelectedTree() {
1288
        return selectedTree;
1289
    }
1290

    
1291
    public void setSelectedTree(TreeView selected) {
1292
        selectedTree = selected;
1293
    }
1294

    
1295
    public Folder getSelection() {
1296
        if(selectedTree != null) {
1297
            return selectedTree.getSelection();
1298
        }
1299
        return null;
1300
    }
1301

    
1302
    public void showFolderStatistics(int folderFileCount) {
1303
        numOfFiles.setHTML(String.valueOf(folderFileCount));
1304
    }
1305

    
1306
    public GroupTreeView getGroupTreeView() {
1307
        return groupTreeView;
1308
    }
1309

    
1310
    public void sessionExpired() {
1311
        new SessionExpiredDialog(this).center();
1312
    }
1313

    
1314
    public void updateRootFolder(Command callback) {
1315
        updateFolder(account.getPithos(), false, callback, true);
1316
    }
1317

    
1318
    void createMySharedTree() {
1319
        LOG("Pithos::createMySharedTree()");
1320
        mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1321
        mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1322
            @Override
1323
            public void onSelectionChange(SelectionChangeEvent event) {
1324
                if(mysharedTreeSelectionModel.getSelectedObject() != null) {
1325
                    deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1326
                    upload.setEnabled(false);
1327
                    disableUploadArea();
1328
                    updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1329
                    showRelevantToolbarButtons();
1330
                }
1331
                else {
1332
                    if(getSelectedTree().equals(mysharedTreeView)) {
1333
                        setSelectedTree(null);
1334
                    }
1335
                    if(getSelectedTree() == null) {
1336
                        showRelevantToolbarButtons();
1337
                    }
1338
                }
1339
            }
1340
        });
1341
        selectionModels.add(mysharedTreeSelectionModel);
1342
        mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1343
        mysharedTreeViewModel.initialize(new Command() {
1344

    
1345
            @Override
1346
            public void execute() {
1347
                mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1348
                trees.insert(mysharedTreeView, 2);
1349
                treeViews.add(mysharedTreeView);
1350
                createOtherSharedTree();
1351
            }
1352
        });
1353
    }
1354

    
1355
    void createOtherSharedTree() {
1356
        LOG("Pithos::createOtherSharedTree()");
1357
        otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1358
        otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1359
            @Override
1360
            public void onSelectionChange(SelectionChangeEvent event) {
1361
                if(otherSharedTreeSelectionModel.getSelectedObject() != null) {
1362
                    deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1363
                    applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1364
                    updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1365
                    showRelevantToolbarButtons();
1366
                }
1367
                else {
1368
                    if(getSelectedTree().equals(otherSharedTreeView)) {
1369
                        setSelectedTree(null);
1370
                    }
1371
                    if(getSelectedTree() == null) {
1372
                        showRelevantToolbarButtons();
1373
                    }
1374
                }
1375
            }
1376
        });
1377
        selectionModels.add(otherSharedTreeSelectionModel);
1378
        otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1379
        // #3784 We show it empty...
1380
        otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel, true);
1381
        trees.insert(otherSharedTreeView, 1);
1382

    
1383
        LOG("Pithos::createOtherSharedTree(), initializing otherSharedTreeViewModel with a callback");
1384
        otherSharedTreeViewModel.initialize(new Command() {
1385
            @Override
1386
            public void execute() {
1387
                // #3784 ... then remove the empty stuff and add a new view with the populated model
1388
                trees.remove(otherSharedTreeView);
1389

    
1390
                otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel, false);
1391
                trees.insert(otherSharedTreeView, 1);
1392
                treeViews.add(otherSharedTreeView);
1393
                scheduleResfresh();
1394
            }
1395
        });
1396
    }
1397

    
1398
    public String getErrorData() {
1399
        final StringBuilder sb = new StringBuilder();
1400
        final String NL = Const.NL;
1401
        Throwable t = this.error;
1402
        while(t != null) {
1403
            sb.append(t.toString());
1404
            sb.append(NL);
1405
            StackTraceElement[] traces = t.getStackTrace();
1406
            for(StackTraceElement trace : traces) {
1407
                sb.append("  [");
1408
                sb.append(trace.getClassName());
1409
                sb.append("::");
1410
                sb.append(trace.getMethodName());
1411
                sb.append("() at ");
1412
                sb.append(trace.getFileName());
1413
                sb.append(":");
1414
                sb.append(trace.getLineNumber());
1415
                sb.append("]");
1416
                sb.append(NL);
1417
            }
1418
            t = t.getCause();
1419
        }
1420

    
1421
        return sb.toString();
1422
    }
1423

    
1424
    public void setError(Throwable t) {
1425
        error = t;
1426
        LOG(t);
1427
    }
1428

    
1429
    public void showRelevantToolbarButtons() {
1430
        toolbar.showRelevantButtons();
1431
    }
1432

    
1433
    public FileUploadDialog getFileUploadDialog() {
1434
        if(fileUploadDialog == null) {
1435
            fileUploadDialog = new FileUploadDialog(this);
1436
        }
1437
        return fileUploadDialog;
1438
    }
1439

    
1440
    public void hideUploadIndicator() {
1441
        upload.removeStyleName("pithos-uploadButton-loading");
1442
        upload.setTitle("");
1443
    }
1444

    
1445
    public void showUploadIndicator() {
1446
        upload.addStyleName("pithos-uploadButton-loading");
1447
        upload.setTitle("Upload in progress. Click for details.");
1448
    }
1449

    
1450
    public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1451
        if(folder == null) {
1452
            if(callback != null) {
1453
                callback.execute();
1454
            }
1455
        }
1456
        else {
1457
            HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getStorageAPIURL(), folder.getOwnerID(), folder.getUri(), folder) {
1458

    
1459
                @Override
1460
                public void onSuccess(Folder _result) {
1461
                    if(callback != null) {
1462
                        callback.execute();
1463
                    }
1464
                }
1465

    
1466
                @Override
1467
                public void onError(Throwable t) {
1468
                    if(t instanceof RestException) {
1469
                        if(((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1470
                            final String path = folder.getUri();
1471
                            PutRequest newFolder = new PutRequest(getStorageAPIURL(), folder.getOwnerID(), path) {
1472
                                @Override
1473
                                public void onSuccess(Resource _result) {
1474
                                    scheduleFolderHeadCommand(folder, callback);
1475
                                }
1476

    
1477
                                @Override
1478
                                public void onError(Throwable _t) {
1479
                                    setError(_t);
1480
                                    if(_t instanceof RestException) {
1481
                                        displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1482
                                    }
1483
                                    else {
1484
                                        displayError("System error creating folder: " + _t.getMessage());
1485
                                    }
1486
                                }
1487

    
1488
                                @Override
1489
                                protected void onUnauthorized(Response response) {
1490
                                    sessionExpired();
1491
                                }
1492
                            };
1493
                            newFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1494
                            newFolder.setHeader(Const.CONTENT_TYPE, "application/folder");
1495
                            newFolder.setHeader(Const.ACCEPT, "*/*");
1496
                            newFolder.setHeader(Const.CONTENT_LENGTH, "0");
1497
                            Scheduler.get().scheduleDeferred(newFolder);
1498
                        }
1499
                        else if(((RestException) t).getHttpStatusCode() == Response.SC_FORBIDDEN) {
1500
                            onSuccess(folder);
1501
                        }
1502
                        else {
1503
                            displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1504
                        }
1505
                    }
1506
                    else {
1507
                        displayError("System error heading folder: " + t.getMessage());
1508
                    }
1509

    
1510
                    LOG("Error heading folder", t);
1511
                    setError(t);
1512
                }
1513

    
1514
                @Override
1515
                protected void onUnauthorized(Response response) {
1516
                    sessionExpired();
1517
                }
1518
            };
1519
            headFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1520
            Scheduler.get().scheduleDeferred(headFolder);
1521
        }
1522
    }
1523

    
1524
    public void scheduleFileHeadCommand(File f, final Command callback) {
1525
        HeadRequest<File> headFile = new HeadRequest<File>(File.class, getStorageAPIURL(), f.getOwnerID(), f.getUri(), f) {
1526

    
1527
            @Override
1528
            public void onSuccess(File _result) {
1529
                if(callback != null) {
1530
                    callback.execute();
1531
                }
1532
            }
1533

    
1534
            @Override
1535
            public void onError(Throwable t) {
1536
                LOG("Error heading file", t);
1537
                setError(t);
1538
                if(t instanceof RestException) {
1539
                    displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1540
                }
1541
                else {
1542
                    displayError("System error heading file: " + t.getMessage());
1543
                }
1544
            }
1545

    
1546
            @Override
1547
            protected void onUnauthorized(Response response) {
1548
                sessionExpired();
1549
            }
1550
        };
1551
        headFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1552
        Scheduler.get().scheduleDeferred(headFile);
1553
    }
1554

    
1555
    public boolean isMySharedSelected() {
1556
        return getSelectedTree().equals(getMySharedTreeView());
1557
    }
1558

    
1559
    private Folder getUploadFolder() {
1560
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1561
            return getSelection();
1562
        }
1563
        return null;
1564
    }
1565

    
1566
    private void updateUploadFolder() {
1567
        updateUploadFolder(null);
1568
    }
1569

    
1570
    private void updateUploadFolder(final JsArrayString urls) {
1571
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1572
            Folder f = getSelection();
1573
            if(getSelectedTree().equals(getFolderTreeView())) {
1574
                updateFolder(f, true, new Command() {
1575

    
1576
                    @Override
1577
                    public void execute() {
1578
                        updateStatistics();
1579
                        if(urls != null) {
1580
                            selectUploadedFiles(urls);
1581
                        }
1582
                    }
1583
                }, false);
1584
            }
1585
            else {
1586
                updateOtherSharedFolder(f, true, null);
1587
            }
1588
        }
1589
    }
1590

    
1591
    public native void disableUploadArea() /*-{
1592
      var uploader = $wnd.$("#uploader").pluploadQueue();
1593
      var dropElm = $wnd.document.getElementById('rightPanel');
1594
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1595
    }-*/;
1596

    
1597
    public native void enableUploadArea() /*-{
1598
      var uploader = $wnd.$("#uploader").pluploadQueue();
1599
      var dropElm = $wnd.document.getElementById('rightPanel');
1600
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1601
      if (uploader.runtime == 'html5') {
1602
        uploader.settings.drop_element = 'rightPanel';
1603
        uploader.trigger('PostInit');
1604
      }
1605
    }-*/;
1606

    
1607
    public void showUploadAlert(int nOfFiles) {
1608
        if(uploadAlert == null) {
1609
            uploadAlert = new UploadAlert(this, nOfFiles);
1610
        }
1611
        if(!uploadAlert.isShowing()) {
1612
            uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1613

    
1614
                @Override
1615
                public void setPosition(int offsetWidth, int offsetHeight) {
1616
                    uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth) / 2, statusPanel.getAbsoluteTop() - offsetHeight);
1617
                }
1618
            });
1619
        }
1620
        uploadAlert.setNumOfFiles(nOfFiles);
1621
    }
1622

    
1623
    public void hideUploadAlert() {
1624
        if(uploadAlert != null && uploadAlert.isShowing()) {
1625
            uploadAlert.hide();
1626
        }
1627
    }
1628

    
1629
    public void selectUploadedFiles(JsArrayString urls) {
1630
        List<String> selectedUrls = new ArrayList<String>();
1631
        for(int i = 0; i < urls.length(); i++) {
1632
            selectedUrls.add(urls.get(i));
1633
        }
1634
        fileList.selectByUrl(selectedUrls);
1635
    }
1636

    
1637
    public void purgeContainer(final Folder container) {
1638
        String path = "/" + container.getName() + "?delimiter=/";
1639
        DeleteRequest delete = new DeleteRequest(getStorageAPIURL(), getUserID(), path) {
1640

    
1641
            @Override
1642
            protected void onUnauthorized(Response response) {
1643
                sessionExpired();
1644
            }
1645

    
1646
            @Override
1647
            public void onSuccess(Resource result) {
1648
                updateFolder(container, true, null, true);
1649
            }
1650

    
1651
            @Override
1652
            public void onError(Throwable t) {
1653
                LOG("Error deleting trash", t);
1654
                setError(t);
1655
                if(t instanceof RestException) {
1656
                    displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1657
                }
1658
                else {
1659
                    displayError("System error deleting trash: " + t.getMessage());
1660
                }
1661
            }
1662
        };
1663
        delete.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1664
        Scheduler.get().scheduleDeferred(delete);
1665
    }
1666
}