Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (59.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 getFromOtherPropertiesOrDefault(String key, String def) {
103
        try {
104
            final String value = otherProperties.get(key);
105
            return value == null ? def : value;
106
        }
107
        catch(Exception e) {
108
            return def;
109
        }
110
    }
111

    
112
    public static String getFromOtherPropertiesOrNull(String key) {
113
        return getFromOtherPropertiesOrDefault(key, null);
114
    }
115

    
116
    public static final String OTHERPROPS_STORAGE_API_URL = getFromOtherPropertiesOrNull("STORAGE_API_URL");
117
    public static final String OTHERPROPS_USER_CATALOGS_API_URL = getFromOtherPropertiesOrNull("USER_CATALOGS_API_URL");
118
    static {
119
        LOG("STORAGE_API_URL = ", OTHERPROPS_STORAGE_API_URL);
120
        LOG("USER_CATALOGS_API_URL = ", OTHERPROPS_USER_CATALOGS_API_URL);
121
    }
122

    
123
    public static final String STORAGE_API_URL;
124
    static {
125
        if(OTHERPROPS_STORAGE_API_URL != null) {
126
            STORAGE_API_URL = OTHERPROPS_STORAGE_API_URL;
127
        }
128
        else if(CONFIG_API_PATH != null) {
129
            STORAGE_API_URL = CONFIG_API_PATH;
130
        }
131
        else {
132
            throw new RuntimeException("Unknown STORAGE_API_URL");
133
        }
134

    
135
        LOG("Computed STORAGE_API_URL = ", STORAGE_API_URL);
136
    }
137

    
138
    public static final String STORAGE_VIEW_URL;
139
    static {
140
        final String viewURL = getFromOtherPropertiesOrNull("STORAGE_VIEW_URL");
141
        if(viewURL != null) {
142
            STORAGE_VIEW_URL = viewURL;
143
        }
144
        else {
145
            STORAGE_VIEW_URL = STORAGE_API_URL;
146
        }
147

    
148
        LOG("Computed STORAGE_VIEW_URL = ", STORAGE_VIEW_URL);
149
    }
150

    
151
    public static final String PUBLIC_LINK_VIEW_PREFIX = getFromOtherPropertiesOrDefault("PUBLIC_LINK_VIEW_PREFIX", "");
152

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

    
169
            USER_CATALOGS_API_URL = url;
170

    
171
            LOG("Computed USER_CATALOGS_API_URL = ", USER_CATALOGS_API_URL);
172
        }
173
    }
174

    
175
    public interface Style extends CssResource {
176
        String commandAnchor();
177

    
178
        String statistics();
179

    
180
        @ClassName("gwt-HTML")
181
        String html();
182

    
183
        String uploadAlert();
184

    
185
        String uploadAlertLink();
186

    
187
        String uploadAlertProgress();
188

    
189
        String uploadAlertPercent();
190

    
191
        String uploadAlertClose();
192
    }
193

    
194
    public interface Resources extends ClientBundle {
195
        @Source("Pithos.css")
196
        Style pithosCss();
197

    
198
        @Source("gr/grnet/pithos/resources/close-popup.png")
199
        ImageResource closePopup();
200
    }
201

    
202
    public static Resources resources = GWT.create(Resources.class);
203

    
204
    /**
205
     * Instantiate an application-level image bundle. This object will provide
206
     * programmatic access to all the images needed by widgets.
207
     */
208
    static Images images = (Images) GWT.create(Images.class);
209

    
210
    public String getUserID() {
211
        return userID;
212
    }
213

    
214
    public UserCatalogs getUserCatalogs() {
215
        return userCatalogs;
216
    }
217

    
218
    public String getCurrentUserDisplayNameOrID() {
219
        final String displayName = userCatalogs.getDisplayName(getUserID());
220
        return displayName == null ? getUserID() : displayName;
221
    }
222

    
223
    public boolean hasDisplayNameForUserID(String userID) {
224
        return userCatalogs.getDisplayName(userID) != null;
225
    }
226

    
227
    public boolean hasIDForUserDisplayName(String userDisplayName) {
228
        return userCatalogs.getID(userDisplayName) != null;
229
    }
230

    
231
    public String getDisplayNameForUserID(String userID) {
232
        return userCatalogs.getDisplayName(userID);
233
    }
234

    
235
    public String getIDForUserDisplayName(String userDisplayName) {
236
        return userCatalogs.getID(userDisplayName);
237
    }
238

    
239
    public List<String> getDisplayNamesForUserIDs(List<String> userIDs) {
240
        if(userIDs == null) {
241
            userIDs = new ArrayList<String>();
242
        }
243
        final List<String> userDisplayNames = new ArrayList<String>();
244
        for(String userID : userIDs) {
245
            final String displayName = getDisplayNameForUserID(userID);
246
            userDisplayNames.add(displayName);
247
        }
248

    
249
        return userDisplayNames;
250
    }
251

    
252
    public List<String> filterUserIDsWithUnknownDisplayName(Collection<String> userIDs) {
253
        if(userIDs == null) {
254
            userIDs = new ArrayList<String>();
255
        }
256
        final List<String> filtered = new ArrayList<String>();
257
        for(String userID : userIDs) {
258
            if(!this.userCatalogs.hasID(userID)) {
259
                filtered.add(userID);
260
            }
261
        }
262
        return filtered;
263
    }
264

    
265
    public void setAccount(AccountResource acct) {
266
        account = acct;
267
    }
268

    
269
    public AccountResource getAccount() {
270
        return account;
271
    }
272

    
273
    public void updateFolder(Folder f, boolean showfiles, Command callback, final boolean openParent) {
274
        folderTreeView.updateFolder(f, showfiles, callback, openParent);
275
    }
276

    
277
    public void updateGroupNode(Group group) {
278
        groupTreeView.updateGroupNode(group);
279
    }
280

    
281
    public void updateMySharedRoot() {
282
        mysharedTreeView.updateRoot();
283
    }
284

    
285
    public void updateSharedFolder(Folder f, boolean showfiles, Command callback) {
286
        mysharedTreeView.updateFolder(f, showfiles, callback);
287
    }
288

    
289
    public void updateSharedFolder(Folder f, boolean showfiles) {
290
        updateSharedFolder(f, showfiles, null);
291
    }
292

    
293
    public void updateOtherSharedFolder(Folder f, boolean showfiles, Command callback) {
294
        otherSharedTreeView.updateFolder(f, showfiles, callback);
295
    }
296

    
297
    public MysharedTreeView getMySharedTreeView() {
298
        return mysharedTreeView;
299
    }
300

    
301
    /**
302
     * An aggregate image bundle that pulls together all the images for this
303
     * application into a single bundle.
304
     */
305
    public interface Images extends TopPanel.Images, FileList.Images, ToolsMenu.Images {
306

    
307
        @Source("gr/grnet/pithos/resources/document.png")
308
        ImageResource folders();
309

    
310
        @Source("gr/grnet/pithos/resources/advancedsettings.png")
311
        @ImageOptions(width = 32, height = 32)
312
        ImageResource tools();
313
    }
314

    
315
    private Throwable error;
316

    
317
    /**
318
     * The Application Clipboard implementation;
319
     */
320
    private Clipboard clipboard = new Clipboard();
321

    
322
    /**
323
     * The top panel that contains the menu bar.
324
     */
325
    private TopPanel topPanel;
326

    
327
    /**
328
     * The panel that contains the various system messages.
329
     */
330
    private MessagePanel messagePanel = new MessagePanel(this, Pithos.images);
331

    
332
    /**
333
     * The bottom panel that contains the status bar.
334
     */
335
    StatusPanel statusPanel = null;
336

    
337
    /**
338
     * The file list widget.
339
     */
340
    private FileList fileList;
341

    
342
    /**
343
     * The tab panel that occupies the right side of the screen.
344
     */
345
    private VerticalPanel inner = new VerticalPanel();
346

    
347

    
348
    /**
349
     * The split panel that will contain the left and right panels.
350
     */
351
    private HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
352

    
353
    /**
354
     * The currently selected item in the application, for use by the Edit menu
355
     * commands. Potential types are Folder, File, User and Group.
356
     */
357
    private Object currentSelection;
358

    
359
    public HashMap<String, String> userFullNameMap = new HashMap<String, String>();
360

    
361
    /**
362
     * The ID that uniquely identifies the user in Pithos+.
363
     * Currently this is a UUID. It used to be the user's email.
364
     */
365
    private String userID = null;
366

    
367
    /**
368
     * Holds mappings from user UUIDs to emails and vice-versa.
369
     */
370
    private UserCatalogs userCatalogs = new UserCatalogs();
371

    
372
    /**
373
     * The authentication token of the current user.
374
     */
375
    private String userToken;
376

    
377
    VerticalPanel trees;
378

    
379
    SingleSelectionModel<Folder> folderTreeSelectionModel;
380
    FolderTreeViewModel folderTreeViewModel;
381
    FolderTreeView folderTreeView;
382

    
383
    SingleSelectionModel<Folder> mysharedTreeSelectionModel;
384
    MysharedTreeViewModel mysharedTreeViewModel;
385
    MysharedTreeView mysharedTreeView = null;
386

    
387
    protected SingleSelectionModel<Folder> otherSharedTreeSelectionModel;
388
    OtherSharedTreeViewModel otherSharedTreeViewModel;
389
    OtherSharedTreeView otherSharedTreeView = null;
390

    
391
    GroupTreeViewModel groupTreeViewModel;
392
    GroupTreeView groupTreeView;
393

    
394
    TreeView selectedTree;
395
    protected AccountResource account;
396

    
397
    Folder trash;
398

    
399
    List<Composite> treeViews = new ArrayList<Composite>();
400

    
401
    @SuppressWarnings("rawtypes")
402
    List<SingleSelectionModel> selectionModels = new ArrayList<SingleSelectionModel>();
403

    
404
    public Button upload;
405

    
406
    private HTML numOfFiles;
407

    
408
    private Toolbar toolbar;
409

    
410
    private FileUploadDialog fileUploadDialog = new FileUploadDialog(this);
411

    
412
    UploadAlert uploadAlert;
413

    
414
    Date lastModified;
415

    
416
    @Override
417
    public void onModuleLoad() {
418
        if(parseUserCredentials()) {
419
            initialize();
420
        }
421
    }
422

    
423
    static native void __ConsoleLog(String message) /*-{
424
      try { console.log(message); } catch (e) {}
425
    }-*/;
426

    
427
    public static void LOGError(Throwable error, StringBuilder sb) {
428
        if(!isLOGEnabled()) { return; }
429

    
430
        sb.append("\nException: [" + error.toString().replace("\n", "\n  ") + "]");
431
        Throwable cause = error.getCause();
432
        if(cause != null) {
433
            sb.append("\nCauses:\n");
434
            while(cause != null) {
435
                sb.append("  ");
436
                sb.append("[" + cause.toString().replace("\n", "\n  ")  + "]");
437
                sb.append("\n");
438
                cause = cause.getCause();
439
            }
440
        }
441
        else {
442
            sb.append("\n");
443
        }
444

    
445
        StackTraceElement[] stackTrace = error.getStackTrace();
446
        sb.append("Stack trace (" + stackTrace.length + " elements):\n");
447
        for(int i = 0; i < stackTrace.length; i++) {
448
            StackTraceElement errorElem = stackTrace[i];
449
            sb.append("  [" + i + "] ");
450
            sb.append(errorElem.toString());
451
            sb.append("\n");
452
        }
453
    }
454

    
455
    public static void LOGError(Throwable error) {
456
        if(!isLOGEnabled()) { return; }
457

    
458
        final StringBuilder sb = new StringBuilder();
459
        LOGError(error, sb);
460
        if(sb.length() > 0) {
461
            __ConsoleLog(sb.toString());
462
        }
463
    }
464

    
465
    public static boolean isLOGEnabled() {
466
        return IsLOGEnabled;
467
    }
468

    
469
    public static void LOG(Object ...args) {
470
        if(!isLOGEnabled()) { return; }
471

    
472
        final StringBuilder sb = new StringBuilder();
473
        for(Object arg : args) {
474
            if(arg instanceof Throwable) {
475
                LOGError((Throwable) arg, sb);
476
            }
477
            else {
478
                sb.append(arg);
479
            }
480
        }
481

    
482
        if(sb.length() > 0) {
483
            __ConsoleLog(sb.toString());
484
        }
485
    }
486

    
487
    private void initialize() {
488
        userCatalogs.updateWithIDAndName("*", "All Pithos users");
489

    
490
        lastModified = new Date(); //Initialize if-modified-since value with now.
491
        resources.pithosCss().ensureInjected();
492
        boolean bareContent = Window.Location.getParameter("noframe") != null;
493
        String contentWidth = bareContent ? Const.PERCENT_100 : Const.PERCENT_75;
494

    
495
        VerticalPanel outer = new VerticalPanel();
496
        outer.setWidth(Const.PERCENT_100);
497
        if(!bareContent) {
498
            outer.addStyleName("pithos-outer");
499
        }
500

    
501
        if(!bareContent) {
502
            topPanel = new TopPanel(this, Pithos.images);
503
            topPanel.setWidth(Const.PERCENT_100);
504
            outer.add(topPanel);
505
            outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
506
        }
507

    
508
        messagePanel.setVisible(false);
509
        outer.add(messagePanel);
510
        outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
511
        outer.setCellVerticalAlignment(messagePanel, HasVerticalAlignment.ALIGN_MIDDLE);
512

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

    
532
        toolbar = new Toolbar(this);
533
        header.add(toolbar);
534
        header.setCellHorizontalAlignment(toolbar, HasHorizontalAlignment.ALIGN_CENTER);
535
        header.setCellVerticalAlignment(toolbar, HasVerticalAlignment.ALIGN_MIDDLE);
536

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

    
555
        folderTreeSelectionModel = new SingleSelectionModel<Folder>();
556
        folderTreeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
557
            @Override
558
            public void onSelectionChange(SelectionChangeEvent event) {
559
                if(folderTreeSelectionModel.getSelectedObject() != null) {
560
                    deselectOthers(folderTreeView, folderTreeSelectionModel);
561
                    applyPermissions(folderTreeSelectionModel.getSelectedObject());
562
                    Folder f = folderTreeSelectionModel.getSelectedObject();
563
                    updateFolder(f, true, new Command() {
564

    
565
                        @Override
566
                        public void execute() {
567
                            updateStatistics();
568
                        }
569
                    }, true);
570
                    showRelevantToolbarButtons();
571
                }
572
                else {
573
                    if(getSelectedTree().equals(folderTreeView)) {
574
                        setSelectedTree(null);
575
                    }
576
                    if(getSelectedTree() == null) {
577
                        showRelevantToolbarButtons();
578
                    }
579
                }
580
            }
581
        });
582
        selectionModels.add(folderTreeSelectionModel);
583

    
584
        folderTreeViewModel = new FolderTreeViewModel(this, folderTreeSelectionModel);
585
        folderTreeView = new FolderTreeView(folderTreeViewModel);
586
        treeViews.add(folderTreeView);
587

    
588
        fileList = new FileList(this, images);
589
        inner.add(fileList);
590

    
591
        trees = new VerticalPanel();
592
        trees.setWidth(Const.PERCENT_100);
593

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

    
607
        if(!bareContent) {
608
            statusPanel = new StatusPanel();
609
            statusPanel.setWidth(Const.PERCENT_100);
610
            outer.add(statusPanel);
611
            outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
612
        }
613
        else {
614
            splitPanel.addStyleName("pithos-splitPanel-noframe");
615
        }
616

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

    
630
            @Override
631
            public boolean execute() {
632
                if(!isCloudbarReady()) {
633
                    return true;
634
                }
635
                onWindowResized(Window.getClientHeight());
636
                return false;
637
            }
638
        });
639

    
640
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
641
            @Override
642
            public void execute() {
643
                LOG("Pithos::initialize() Calling Pithos::fetchAccount()");
644
                fetchAccount(new Command() {
645

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

    
664
                                @Override
665
                                public void execute() {
666
                                    createMySharedTree();
667
                                }
668
                            });
669

    
670
                            HorizontalPanel separator = new HorizontalPanel();
671
                            separator.addStyleName("pithos-statisticsSeparator");
672
                            separator.add(new HTML(""));
673
                            trees.add(separator);
674

    
675
                            groupTreeViewModel = new GroupTreeViewModel(Pithos.this);
676
                            groupTreeView = new GroupTreeView(groupTreeViewModel);
677
                            treeViews.add(groupTreeView);
678
                            trees.add(groupTreeView);
679
                            folderTreeView.showStatistics(account);
680
                        }
681
                    }
682
                });
683
            }
684
        });
685
    }
686

    
687
    public void scheduleResfresh() {
688
        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
689

    
690
            @Override
691
            public boolean execute() {
692
                final Folder f = getSelection();
693
                if(f == null) {
694
                    return true;
695
                }
696

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

    
699
                    @Override
700
                    public void onSuccess(Folder _result) {
701
                        lastModified = new Date();
702
                        if(getSelectedTree().equals(folderTreeView)) {
703
                            updateFolder(f, true, new Command() {
704

    
705
                                @Override
706
                                public void execute() {
707
                                    scheduleResfresh();
708
                                }
709

    
710
                            }, false);
711
                        }
712
                        else if(getSelectedTree().equals(mysharedTreeView)) {
713
                            updateSharedFolder(f, true, new Command() {
714

    
715
                                @Override
716
                                public void execute() {
717
                                    scheduleResfresh();
718
                                }
719
                            });
720
                        }
721
                        else {
722
                            scheduleResfresh();
723
                        }
724
                    }
725

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

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

    
762
                return false;
763
            }
764
        }, 3000);
765
    }
766

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

    
791
    @SuppressWarnings({"rawtypes", "unchecked"})
792
    public void deselectOthers(TreeView _selectedTree, SingleSelectionModel model) {
793
        selectedTree = _selectedTree;
794

    
795
        for(SingleSelectionModel s : selectionModels) {
796
            if(!s.equals(model) && s.getSelectedObject() != null) {
797
                s.setSelected(s.getSelectedObject(), false);
798
            }
799
        }
800
    }
801

    
802
    public void showFiles(final Folder f) {
803
        Set<File> files = f.getFiles();
804
        showFiles(files);
805
    }
806

    
807
    public void showFiles(Set<File> files) {
808
        fileList.setFiles(new ArrayList<File>(files));
809
    }
810

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

    
835
        String gotoUrl = Window.Location.getParameter("goto");
836
        if(gotoUrl != null && gotoUrl.length() > 0) {
837
            Window.Location.assign(gotoUrl);
838
            return false;
839
        }
840
        return true;
841
    }
842

    
843
    /**
844
     * Redirect the user to the login page for authentication.
845
     */
846
    protected void authenticateUser() {
847
        Dictionary otherProperties = Dictionary.getDictionary(Const.OTHER_PROPERTIES);
848
        Window.Location.assign(otherProperties.get(Const.LOGIN_URL) + Window.Location.getHref());
849
    }
850

    
851
    public void fetchAccount(final Command callback) {
852
        String path = "?format=json";
853

    
854
        GetRequest<AccountResource> getAccount = new GetRequest<AccountResource>(AccountResource.class, getStorageAPIURL(), userID, path) {
855
            @Override
856
            public void onSuccess(AccountResource accountResource) {
857
                account = accountResource;
858
                if(callback != null) {
859
                    callback.execute();
860
                }
861

    
862
                final List<String> memberIDs = new ArrayList<String>();
863
                final List<Group> groups = account.getGroups();
864
                for(Group group : groups) {
865
                    memberIDs.addAll(group.getMemberIDs());
866
                }
867
                memberIDs.add(Pithos.this.getUserID());
868

    
869
                final List<String> theUnknown = Pithos.this.filterUserIDsWithUnknownDisplayName(memberIDs);
870
                // Initialize the user catalog
871
                new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();
872
                LOG("Called new UpdateUserCatalogs(Pithos.this, theUnknown).scheduleDeferred();");
873
            }
874

    
875
            @Override
876
            public void onError(Throwable t) {
877
                LOG("Error getting account", t);
878
                setError(t);
879
                if(t instanceof RestException) {
880
                    displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
881
                }
882
                else {
883
                    displayError("System error fetching user data: " + t.getMessage());
884
                }
885
            }
886

    
887
            @Override
888
            protected void onUnauthorized(Response response) {
889
                sessionExpired();
890
            }
891
        };
892
        getAccount.setHeader(Const.X_AUTH_TOKEN, userToken);
893
        Scheduler.get().scheduleDeferred(getAccount);
894
    }
895

    
896
    public void updateStatistics() {
897
        HeadRequest<AccountResource> headAccount = new HeadRequest<AccountResource>(AccountResource.class, getStorageAPIURL(), userID, "", account) {
898

    
899
            @Override
900
            public void onSuccess(AccountResource _result) {
901
                folderTreeView.showStatistics(account);
902
            }
903

    
904
            @Override
905
            public void onError(Throwable t) {
906
                LOG("Error getting account", t);
907
                setError(t);
908
                if(t instanceof RestException) {
909
                    displayError("Error getting account: " + ((RestException) t).getHttpStatusText());
910
                }
911
                else {
912
                    displayError("System error fetching user data: " + t.getMessage());
913
                }
914
            }
915

    
916
            @Override
917
            protected void onUnauthorized(Response response) {
918
                sessionExpired();
919
            }
920
        };
921
        headAccount.setHeader(Const.X_AUTH_TOKEN, userToken);
922
        Scheduler.get().scheduleDeferred(headAccount);
923
    }
924

    
925
    protected void createHomeContainer(final AccountResource _account, final Command callback) {
926
        String path = "/" + Const.HOME_CONTAINER;
927
        PutRequest createPithos = new PutRequest(getStorageAPIURL(), getUserID(), path) {
928
            @Override
929
            public void onSuccess(Resource result) {
930
                if(!_account.hasTrashContainer()) {
931
                    createTrashContainer(callback);
932
                }
933
                else {
934
                    fetchAccount(callback);
935
                }
936
            }
937

    
938
            @Override
939
            public void onError(Throwable t) {
940
                LOG("Error creating pithos", t);
941
                setError(t);
942
                if(t instanceof RestException) {
943
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
944
                }
945
                else {
946
                    displayError("System error Error creating pithos: " + t.getMessage());
947
                }
948
            }
949

    
950
            @Override
951
            protected void onUnauthorized(Response response) {
952
                sessionExpired();
953
            }
954
        };
955
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
956
        Scheduler.get().scheduleDeferred(createPithos);
957
    }
958

    
959
    protected void createTrashContainer(final Command callback) {
960
        String path = "/" + Const.TRASH_CONTAINER;
961
        PutRequest createPithos = new PutRequest(getStorageAPIURL(), getUserID(), path) {
962
            @Override
963
            public void onSuccess(Resource result) {
964
                fetchAccount(callback);
965
            }
966

    
967
            @Override
968
            public void onError(Throwable t) {
969
                LOG("Error creating pithos", t);
970
                setError(t);
971
                if(t instanceof RestException) {
972
                    displayError("Error creating pithos: " + ((RestException) t).getHttpStatusText());
973
                }
974
                else {
975
                    displayError("System error Error creating pithos: " + t.getMessage());
976
                }
977
            }
978

    
979
            @Override
980
            protected void onUnauthorized(Response response) {
981
                sessionExpired();
982
            }
983
        };
984
        createPithos.setHeader(Const.X_AUTH_TOKEN, getUserToken());
985
        Scheduler.get().scheduleDeferred(createPithos);
986
    }
987

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

    
1004
    protected void onWindowResized(int height) {
1005
        // Adjust the split panel to take up the available room in the window.
1006
        int newHeight = height - splitPanel.getAbsoluteTop() - 153;
1007
        if(newHeight < 1) {
1008
            newHeight = 1;
1009
        }
1010
        splitPanel.setHeight("" + newHeight);
1011
        inner.setHeight("" + newHeight);
1012
    }
1013

    
1014
    native boolean isCloudbarReady()/*-{
1015
      if ($wnd.$("div.cloudbar") && $wnd.$("div.cloudbar").height() > 0)
1016
        return true;
1017
      return false;
1018
    }-*/;
1019

    
1020
    @Override
1021
    public void onResize(ResizeEvent event) {
1022
        int height = event.getHeight();
1023
        onWindowResized(height);
1024
    }
1025

    
1026
    /**
1027
     * Display an error message.
1028
     *
1029
     * @param msg the message to display
1030
     */
1031
    public void displayError(String msg) {
1032
        messagePanel.displayError(msg);
1033
        onWindowResized(Window.getClientHeight());
1034
    }
1035

    
1036
    /**
1037
     * Display a warning message.
1038
     *
1039
     * @param msg the message to display
1040
     */
1041
    public void displayWarning(String msg) {
1042
        messagePanel.displayWarning(msg);
1043
        onWindowResized(Window.getClientHeight());
1044
    }
1045

    
1046
    /**
1047
     * Display an informational message.
1048
     *
1049
     * @param msg the message to display
1050
     */
1051
    public void displayInformation(String msg) {
1052
        messagePanel.displayInformation(msg);
1053
        onWindowResized(Window.getClientHeight());
1054
    }
1055

    
1056
    /**
1057
     * Retrieve the fileList.
1058
     *
1059
     * @return the fileList
1060
     */
1061
    public FileList getFileList() {
1062
        return fileList;
1063
    }
1064

    
1065
    /**
1066
     * Retrieve the topPanel.
1067
     *
1068
     * @return the topPanel
1069
     */
1070
    TopPanel getTopPanel() {
1071
        return topPanel;
1072
    }
1073

    
1074
    /**
1075
     * Retrieve the clipboard.
1076
     *
1077
     * @return the clipboard
1078
     */
1079
    public Clipboard getClipboard() {
1080
        return clipboard;
1081
    }
1082

    
1083
    public StatusPanel getStatusPanel() {
1084
        return statusPanel;
1085
    }
1086

    
1087
    public String getUserToken() {
1088
        return userToken;
1089
    }
1090

    
1091
    public static native void preventIESelection() /*-{
1092
      $doc.body.onselectstart = function () {
1093
        return false;
1094
      };
1095
    }-*/;
1096

    
1097
    public static native void enableIESelection() /*-{
1098
      if ($doc.body.onselectstart != null)
1099
        $doc.body.onselectstart = null;
1100
    }-*/;
1101

    
1102
    public static String getStorageAPIURL() {
1103
        return STORAGE_API_URL;
1104
    }
1105

    
1106
    public static String getStorageViewURL() {
1107
        return STORAGE_VIEW_URL;
1108
    }
1109

    
1110
    public static String getUserCatalogsURL() {
1111
        return USER_CATALOGS_API_URL;
1112
    }
1113

    
1114
    public static String getFileViewURL(File file) {
1115
        return Pithos.getStorageViewURL() + file.getOwnerID() + file.getUri();
1116
    }
1117

    
1118
    /**
1119
     * History support for folder navigation
1120
     * adds a new browser history entry
1121
     *
1122
     * @param key
1123
     */
1124
    public void updateHistory(String key) {
1125
//                Replace any whitespace of the initial string to "+"
1126
//                String result = key.replaceAll("\\s","+");
1127
//                Add a new browser history entry.
1128
//                History.newItem(result);
1129
        History.newItem(key);
1130
    }
1131

    
1132
    public void deleteFolder(final Folder folder, final Command callback) {
1133
        final PleaseWaitPopup pwp = new PleaseWaitPopup();
1134
        pwp.center();
1135
        String path = "/" + folder.getContainer() + "/" + folder.getPrefix() + "?delimiter=/" + "&t=" + System.currentTimeMillis();
1136
        DeleteRequest deleteFolder = new DeleteRequest(getStorageAPIURL(), folder.getOwnerID(), path) {
1137

    
1138
            @Override
1139
            protected void onUnauthorized(Response response) {
1140
                pwp.hide();
1141
                sessionExpired();
1142
            }
1143

    
1144
            @Override
1145
            public void onSuccess(Resource result) {
1146
                updateFolder(folder.getParent(), true, new Command() {
1147

    
1148
                    @Override
1149
                    public void execute() {
1150
                        folderTreeSelectionModel.setSelected(folder.getParent(), true);
1151
                        updateStatistics();
1152
                        if(callback != null) {
1153
                            callback.execute();
1154
                        }
1155
                        pwp.hide();
1156
                    }
1157
                }, true);
1158
            }
1159

    
1160
            @Override
1161
            public void onError(Throwable t) {
1162
                LOG(t);
1163
                setError(t);
1164
                if(t instanceof RestException) {
1165
                    if(((RestException) t).getHttpStatusCode() != Response.SC_NOT_FOUND) {
1166
                        displayError("Unable to delete folder: " + ((RestException) t).getHttpStatusText());
1167
                    }
1168
                    else {
1169
                        onSuccess(null);
1170
                    }
1171
                }
1172
                else {
1173
                    displayError("System error unable to delete folder: " + t.getMessage());
1174
                }
1175
                pwp.hide();
1176
            }
1177
        };
1178
        deleteFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1179
        Scheduler.get().scheduleDeferred(deleteFolder);
1180
    }
1181

    
1182
    public FolderTreeView getFolderTreeView() {
1183
        return folderTreeView;
1184
    }
1185

    
1186
    public void copyFiles(final Iterator<File> iter, final String targetUsername, final String targetUri, final Command callback) {
1187
        if(iter.hasNext()) {
1188
            File file = iter.next();
1189
            String path = targetUri + "/" + file.getName();
1190
            PutRequest copyFile = new PutRequest(getStorageAPIURL(), targetUsername, path) {
1191
                @Override
1192
                public void onSuccess(Resource result) {
1193
                    copyFiles(iter, targetUsername, targetUri, callback);
1194
                }
1195

    
1196
                @Override
1197
                public void onError(Throwable t) {
1198
                    LOG(t);
1199
                    setError(t);
1200
                    if(t instanceof RestException) {
1201
                        displayError("Unable to copy file: " + ((RestException) t).getHttpStatusText());
1202
                    }
1203
                    else {
1204
                        displayError("System error unable to copy file: " + t.getMessage());
1205
                    }
1206
                }
1207

    
1208
                @Override
1209
                protected void onUnauthorized(Response response) {
1210
                    sessionExpired();
1211
                }
1212
            };
1213
            copyFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1214
            copyFile.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(file.getUri()));
1215
            if(!file.getOwnerID().equals(targetUsername)) {
1216
                copyFile.setHeader(Const.X_SOURCE_ACCOUNT, URL.encodePathSegment(file.getOwnerID()));
1217
            }
1218
            copyFile.setHeader(Const.CONTENT_TYPE, file.getContentType());
1219
            Scheduler.get().scheduleDeferred(copyFile);
1220
        }
1221
        else if(callback != null) {
1222
            callback.execute();
1223
        }
1224
    }
1225

    
1226
    public void copyFolder(final Folder f, final String targetUsername, final String targetUri, boolean move, final Command callback) {
1227
        String path = targetUri + "?delimiter=/";
1228
        PutRequest copyFolder = new PutRequest(getStorageAPIURL(), targetUsername, path) {
1229
            @Override
1230
            public void onSuccess(Resource result) {
1231
                if(callback != null) {
1232
                    callback.execute();
1233
                }
1234
            }
1235

    
1236
            @Override
1237
            public void onError(Throwable t) {
1238
                LOG(t);
1239
                setError(t);
1240
                if(t instanceof RestException) {
1241
                    displayError("Unable to copy folder: " + ((RestException) t).getHttpStatusText());
1242
                }
1243
                else {
1244
                    displayError("System error copying folder: " + t.getMessage());
1245
                }
1246
            }
1247

    
1248
            @Override
1249
            protected void onUnauthorized(Response response) {
1250
                sessionExpired();
1251
            }
1252
        };
1253
        copyFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1254
        copyFolder.setHeader(Const.ACCEPT, "*/*");
1255
        copyFolder.setHeader(Const.CONTENT_LENGTH, "0");
1256
        copyFolder.setHeader(Const.CONTENT_TYPE, "application/directory");
1257
        if(!f.getOwnerID().equals(targetUsername)) {
1258
            copyFolder.setHeader(Const.X_SOURCE_ACCOUNT, f.getOwnerID());
1259
        }
1260
        if(move) {
1261
            copyFolder.setHeader(Const.X_MOVE_FROM, URL.encodePathSegment(f.getUri()));
1262
        }
1263
        else {
1264
            copyFolder.setHeader(Const.X_COPY_FROM, URL.encodePathSegment(f.getUri()));
1265
        }
1266
        Scheduler.get().scheduleDeferred(copyFolder);
1267
    }
1268

    
1269
    public void addSelectionModel(@SuppressWarnings("rawtypes") SingleSelectionModel model) {
1270
        selectionModels.add(model);
1271
    }
1272

    
1273
    public OtherSharedTreeView getOtherSharedTreeView() {
1274
        return otherSharedTreeView;
1275
    }
1276

    
1277
    public void updateTrash(boolean showFiles, Command callback) {
1278
        updateFolder(trash, showFiles, callback, true);
1279
    }
1280

    
1281
    public void updateGroupsNode() {
1282
        groupTreeView.updateGroupNode(null);
1283
    }
1284

    
1285
    public Group addGroup(String groupname) {
1286
        Group newGroup = new Group(groupname);
1287
        account.addGroup(newGroup);
1288
        groupTreeView.updateGroupNode(null);
1289
        return newGroup;
1290
    }
1291

    
1292
    public void removeGroup(Group group) {
1293
        account.removeGroup(group);
1294
        updateGroupsNode();
1295
    }
1296

    
1297
    public TreeView getSelectedTree() {
1298
        return selectedTree;
1299
    }
1300

    
1301
    public void setSelectedTree(TreeView selected) {
1302
        selectedTree = selected;
1303
    }
1304

    
1305
    public Folder getSelection() {
1306
        if(selectedTree != null) {
1307
            return selectedTree.getSelection();
1308
        }
1309
        return null;
1310
    }
1311

    
1312
    public void showFolderStatistics(int folderFileCount) {
1313
        numOfFiles.setHTML(String.valueOf(folderFileCount));
1314
    }
1315

    
1316
    public GroupTreeView getGroupTreeView() {
1317
        return groupTreeView;
1318
    }
1319

    
1320
    public void sessionExpired() {
1321
        new SessionExpiredDialog(this).center();
1322
    }
1323

    
1324
    public void updateRootFolder(Command callback) {
1325
        updateFolder(account.getPithos(), false, callback, true);
1326
    }
1327

    
1328
    void createMySharedTree() {
1329
        LOG("Pithos::createMySharedTree()");
1330
        mysharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1331
        mysharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1332
            @Override
1333
            public void onSelectionChange(SelectionChangeEvent event) {
1334
                if(mysharedTreeSelectionModel.getSelectedObject() != null) {
1335
                    deselectOthers(mysharedTreeView, mysharedTreeSelectionModel);
1336
                    upload.setEnabled(false);
1337
                    disableUploadArea();
1338
                    updateSharedFolder(mysharedTreeSelectionModel.getSelectedObject(), true);
1339
                    showRelevantToolbarButtons();
1340
                }
1341
                else {
1342
                    if(getSelectedTree().equals(mysharedTreeView)) {
1343
                        setSelectedTree(null);
1344
                    }
1345
                    if(getSelectedTree() == null) {
1346
                        showRelevantToolbarButtons();
1347
                    }
1348
                }
1349
            }
1350
        });
1351
        selectionModels.add(mysharedTreeSelectionModel);
1352
        mysharedTreeViewModel = new MysharedTreeViewModel(Pithos.this, mysharedTreeSelectionModel);
1353
        mysharedTreeViewModel.initialize(new Command() {
1354

    
1355
            @Override
1356
            public void execute() {
1357
                mysharedTreeView = new MysharedTreeView(mysharedTreeViewModel);
1358
                trees.insert(mysharedTreeView, 2);
1359
                treeViews.add(mysharedTreeView);
1360
                createOtherSharedTree();
1361
            }
1362
        });
1363
    }
1364

    
1365
    void createOtherSharedTree() {
1366
        LOG("Pithos::createOtherSharedTree()");
1367
        otherSharedTreeSelectionModel = new SingleSelectionModel<Folder>();
1368
        otherSharedTreeSelectionModel.addSelectionChangeHandler(new Handler() {
1369
            @Override
1370
            public void onSelectionChange(SelectionChangeEvent event) {
1371
                if(otherSharedTreeSelectionModel.getSelectedObject() != null) {
1372
                    deselectOthers(otherSharedTreeView, otherSharedTreeSelectionModel);
1373
                    applyPermissions(otherSharedTreeSelectionModel.getSelectedObject());
1374
                    updateOtherSharedFolder(otherSharedTreeSelectionModel.getSelectedObject(), true, null);
1375
                    showRelevantToolbarButtons();
1376
                }
1377
                else {
1378
                    if(getSelectedTree().equals(otherSharedTreeView)) {
1379
                        setSelectedTree(null);
1380
                    }
1381
                    if(getSelectedTree() == null) {
1382
                        showRelevantToolbarButtons();
1383
                    }
1384
                }
1385
            }
1386
        });
1387
        selectionModels.add(otherSharedTreeSelectionModel);
1388
        otherSharedTreeViewModel = new OtherSharedTreeViewModel(Pithos.this, otherSharedTreeSelectionModel);
1389
        // #3784 We show it empty...
1390
        otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel, true);
1391
        trees.insert(otherSharedTreeView, 1);
1392

    
1393
        LOG("Pithos::createOtherSharedTree(), initializing otherSharedTreeViewModel with a callback");
1394
        otherSharedTreeViewModel.initialize(new Command() {
1395
            @Override
1396
            public void execute() {
1397
                // #3784 ... then remove the empty stuff and add a new view with the populated model
1398
                trees.remove(otherSharedTreeView);
1399

    
1400
                otherSharedTreeView = new OtherSharedTreeView(otherSharedTreeViewModel, false);
1401
                trees.insert(otherSharedTreeView, 1);
1402
                treeViews.add(otherSharedTreeView);
1403
                scheduleResfresh();
1404
            }
1405
        });
1406
    }
1407

    
1408
    public String getErrorData() {
1409
        final StringBuilder sb = new StringBuilder();
1410
        final String NL = Const.NL;
1411
        Throwable t = this.error;
1412
        while(t != null) {
1413
            sb.append(t.toString());
1414
            sb.append(NL);
1415
            StackTraceElement[] traces = t.getStackTrace();
1416
            for(StackTraceElement trace : traces) {
1417
                sb.append("  [");
1418
                sb.append(trace.getClassName());
1419
                sb.append("::");
1420
                sb.append(trace.getMethodName());
1421
                sb.append("() at ");
1422
                sb.append(trace.getFileName());
1423
                sb.append(":");
1424
                sb.append(trace.getLineNumber());
1425
                sb.append("]");
1426
                sb.append(NL);
1427
            }
1428
            t = t.getCause();
1429
        }
1430

    
1431
        return sb.toString();
1432
    }
1433

    
1434
    public void setError(Throwable t) {
1435
        error = t;
1436
        LOG(t);
1437
    }
1438

    
1439
    public void showRelevantToolbarButtons() {
1440
        toolbar.showRelevantButtons();
1441
    }
1442

    
1443
    public FileUploadDialog getFileUploadDialog() {
1444
        if(fileUploadDialog == null) {
1445
            fileUploadDialog = new FileUploadDialog(this);
1446
        }
1447
        return fileUploadDialog;
1448
    }
1449

    
1450
    public void hideUploadIndicator() {
1451
        upload.removeStyleName("pithos-uploadButton-loading");
1452
        upload.setTitle("");
1453
    }
1454

    
1455
    public void showUploadIndicator() {
1456
        upload.addStyleName("pithos-uploadButton-loading");
1457
        upload.setTitle("Upload in progress. Click for details.");
1458
    }
1459

    
1460
    public void scheduleFolderHeadCommand(final Folder folder, final Command callback) {
1461
        if(folder == null) {
1462
            if(callback != null) {
1463
                callback.execute();
1464
            }
1465
        }
1466
        else {
1467
            HeadRequest<Folder> headFolder = new HeadRequest<Folder>(Folder.class, getStorageAPIURL(), folder.getOwnerID(), folder.getUri(), folder) {
1468

    
1469
                @Override
1470
                public void onSuccess(Folder _result) {
1471
                    if(callback != null) {
1472
                        callback.execute();
1473
                    }
1474
                }
1475

    
1476
                @Override
1477
                public void onError(Throwable t) {
1478
                    if(t instanceof RestException) {
1479
                        if(((RestException) t).getHttpStatusCode() == Response.SC_NOT_FOUND) {
1480
                            final String path = folder.getUri();
1481
                            PutRequest newFolder = new PutRequest(getStorageAPIURL(), folder.getOwnerID(), path) {
1482
                                @Override
1483
                                public void onSuccess(Resource _result) {
1484
                                    scheduleFolderHeadCommand(folder, callback);
1485
                                }
1486

    
1487
                                @Override
1488
                                public void onError(Throwable _t) {
1489
                                    setError(_t);
1490
                                    if(_t instanceof RestException) {
1491
                                        displayError("Unable to create folder: " + ((RestException) _t).getHttpStatusText());
1492
                                    }
1493
                                    else {
1494
                                        displayError("System error creating folder: " + _t.getMessage());
1495
                                    }
1496
                                }
1497

    
1498
                                @Override
1499
                                protected void onUnauthorized(Response response) {
1500
                                    sessionExpired();
1501
                                }
1502
                            };
1503
                            newFolder.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1504
                            newFolder.setHeader(Const.CONTENT_TYPE, "application/folder");
1505
                            newFolder.setHeader(Const.ACCEPT, "*/*");
1506
                            newFolder.setHeader(Const.CONTENT_LENGTH, "0");
1507
                            Scheduler.get().scheduleDeferred(newFolder);
1508
                        }
1509
                        else if(((RestException) t).getHttpStatusCode() == Response.SC_FORBIDDEN) {
1510
                            onSuccess(folder);
1511
                        }
1512
                        else {
1513
                            displayError("Error heading folder: " + ((RestException) t).getHttpStatusText());
1514
                        }
1515
                    }
1516
                    else {
1517
                        displayError("System error heading folder: " + t.getMessage());
1518
                    }
1519

    
1520
                    LOG("Error heading folder", t);
1521
                    setError(t);
1522
                }
1523

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

    
1534
    public void scheduleFileHeadCommand(File f, final Command callback) {
1535
        HeadRequest<File> headFile = new HeadRequest<File>(File.class, getStorageAPIURL(), f.getOwnerID(), f.getUri(), f) {
1536

    
1537
            @Override
1538
            public void onSuccess(File _result) {
1539
                if(callback != null) {
1540
                    callback.execute();
1541
                }
1542
            }
1543

    
1544
            @Override
1545
            public void onError(Throwable t) {
1546
                LOG("Error heading file", t);
1547
                setError(t);
1548
                if(t instanceof RestException) {
1549
                    displayError("Error heading file: " + ((RestException) t).getHttpStatusText());
1550
                }
1551
                else {
1552
                    displayError("System error heading file: " + t.getMessage());
1553
                }
1554
            }
1555

    
1556
            @Override
1557
            protected void onUnauthorized(Response response) {
1558
                sessionExpired();
1559
            }
1560
        };
1561
        headFile.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1562
        Scheduler.get().scheduleDeferred(headFile);
1563
    }
1564

    
1565
    public boolean isMySharedSelected() {
1566
        return getSelectedTree().equals(getMySharedTreeView());
1567
    }
1568

    
1569
    private Folder getUploadFolder() {
1570
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1571
            return getSelection();
1572
        }
1573
        return null;
1574
    }
1575

    
1576
    private void updateUploadFolder() {
1577
        updateUploadFolder(null);
1578
    }
1579

    
1580
    private void updateUploadFolder(final JsArrayString urls) {
1581
        if(folderTreeView.equals(getSelectedTree()) || otherSharedTreeView.equals(getSelectedTree())) {
1582
            Folder f = getSelection();
1583
            if(getSelectedTree().equals(getFolderTreeView())) {
1584
                updateFolder(f, true, new Command() {
1585

    
1586
                    @Override
1587
                    public void execute() {
1588
                        updateStatistics();
1589
                        if(urls != null) {
1590
                            selectUploadedFiles(urls);
1591
                        }
1592
                    }
1593
                }, false);
1594
            }
1595
            else {
1596
                updateOtherSharedFolder(f, true, null);
1597
            }
1598
        }
1599
    }
1600

    
1601
    public native void disableUploadArea() /*-{
1602
      var uploader = $wnd.$("#uploader").pluploadQueue();
1603
      var dropElm = $wnd.document.getElementById('rightPanel');
1604
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1605
    }-*/;
1606

    
1607
    public native void enableUploadArea() /*-{
1608
      var uploader = $wnd.$("#uploader").pluploadQueue();
1609
      var dropElm = $wnd.document.getElementById('rightPanel');
1610
      $wnd.plupload.removeAllEvents(dropElm, uploader.id);
1611
      if (uploader.runtime == 'html5') {
1612
        uploader.settings.drop_element = 'rightPanel';
1613
        uploader.trigger('PostInit');
1614
      }
1615
    }-*/;
1616

    
1617
    public void showUploadAlert(int nOfFiles) {
1618
        if(uploadAlert == null) {
1619
            uploadAlert = new UploadAlert(this, nOfFiles);
1620
        }
1621
        if(!uploadAlert.isShowing()) {
1622
            uploadAlert.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
1623

    
1624
                @Override
1625
                public void setPosition(int offsetWidth, int offsetHeight) {
1626
                    uploadAlert.setPopupPosition((Window.getClientWidth() - offsetWidth) / 2, statusPanel.getAbsoluteTop() - offsetHeight);
1627
                }
1628
            });
1629
        }
1630
        uploadAlert.setNumOfFiles(nOfFiles);
1631
    }
1632

    
1633
    public void hideUploadAlert() {
1634
        if(uploadAlert != null && uploadAlert.isShowing()) {
1635
            uploadAlert.hide();
1636
        }
1637
    }
1638

    
1639
    public void selectUploadedFiles(JsArrayString urls) {
1640
        List<String> selectedUrls = new ArrayList<String>();
1641
        for(int i = 0; i < urls.length(); i++) {
1642
            selectedUrls.add(urls.get(i));
1643
        }
1644
        fileList.selectByUrl(selectedUrls);
1645
    }
1646

    
1647
    public void purgeContainer(final Folder container) {
1648
        String path = "/" + container.getName() + "?delimiter=/";
1649
        DeleteRequest delete = new DeleteRequest(getStorageAPIURL(), getUserID(), path) {
1650

    
1651
            @Override
1652
            protected void onUnauthorized(Response response) {
1653
                sessionExpired();
1654
            }
1655

    
1656
            @Override
1657
            public void onSuccess(Resource result) {
1658
                updateFolder(container, true, null, true);
1659
            }
1660

    
1661
            @Override
1662
            public void onError(Throwable t) {
1663
                LOG("Error deleting trash", t);
1664
                setError(t);
1665
                if(t instanceof RestException) {
1666
                    displayError("Error deleting trash: " + ((RestException) t).getHttpStatusText());
1667
                }
1668
                else {
1669
                    displayError("System error deleting trash: " + t.getMessage());
1670
                }
1671
            }
1672
        };
1673
        delete.setHeader(Const.X_AUTH_TOKEN, getUserToken());
1674
        Scheduler.get().scheduleDeferred(delete);
1675
    }
1676
}