New button "Add everybody" to file Share dialog
[pithos-web-client] / src / gr / grnet / pithos / web / client / FileShareDialog.java
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.http.client.URL;
38 import com.google.gwt.user.client.ui.*;
39 import gr.grnet.pithos.web.client.foldertree.File;
40 import gr.grnet.pithos.web.client.rest.HeadRequest;
41 import gr.grnet.pithos.web.client.rest.PostRequest;
42
43 import com.google.gwt.core.client.GWT;
44 import com.google.gwt.core.client.Scheduler;
45 import com.google.gwt.dom.client.NativeEvent;
46 import com.google.gwt.event.dom.client.ClickEvent;
47 import com.google.gwt.event.dom.client.ClickHandler;
48 import com.google.gwt.event.dom.client.KeyCodes;
49 import com.google.gwt.http.client.Response;
50 import com.google.gwt.http.client.UrlBuilder;
51 import com.google.gwt.resources.client.ImageResource;
52 import com.google.gwt.user.client.Command;
53 import com.google.gwt.user.client.Window;
54 import com.google.gwt.user.client.Event.NativePreviewEvent;
55
56 import java.util.Map;
57
58 /**
59  * UI for the "Share" command.
60  */
61 public class FileShareDialog extends AbstractPropertiesDialog {
62     // For public sharing
63         private final HorizontalPanel publicPathPanel = new HorizontalPanel();
64         private final TextBox publicPathText = new TextBox();
65
66     // For private sharing
67     private final HorizontalPanel privatePathPanel = new HorizontalPanel();
68     private final TextBox privatePathText = new TextBox();
69     private final VerticalPanel privatePermSuperPanel = new VerticalPanel();
70     private PermissionsList permList;
71         
72         /**
73          * An image bundle for this widgets images.
74          */
75         public interface PublicSharingImages extends MessagePanel.Images {
76
77                 @Source("gr/grnet/pithos/resources/edit_user.png")
78                 ImageResource permUser();
79
80                 @Source("gr/grnet/pithos/resources/groups22.png")
81                 ImageResource permGroup();
82
83                 @Source("gr/grnet/pithos/resources/editdelete.png")
84                 ImageResource delete();
85     }
86
87     public interface PrivateSharingImages extends MessagePanel.Images {
88
89         @Source("gr/grnet/pithos/resources/edit_user.png")
90         ImageResource permUser();
91
92         @Source("gr/grnet/pithos/resources/groups22.png")
93         ImageResource permGroup();
94
95         @Source("gr/grnet/pithos/resources/delete.gif")
96         ImageResource delete();
97     }
98
99     private final File file;
100
101     private final PublicSharingImages publicSharingImages = GWT.create(PublicSharingImages.class);
102     private final PrivateSharingImages privateSharingImages = GWT.create(PrivateSharingImages.class);
103
104         /**
105          * The widget's constructor.
106          */
107         public FileShareDialog(Pithos app, File file) {
108         super(app);
109         this.file = file;
110
111                 Anchor close = new Anchor("close");
112                 close.addStyleName("close");
113                 close.addClickHandler(new ClickHandler() {
114                         
115                         @Override
116                         public void onClick(ClickEvent event) {
117                                 hide();
118                         }
119                 });
120                 // Set the dialog's caption.
121                 setText("Share");
122                 setGlassEnabled(true);
123                 setStyleName("pithos-DialogBox");
124
125                 // Outer contains inner and buttons.
126                 final VerticalPanel outer = new VerticalPanel();
127                 outer.add(close);
128                 final FocusPanel focusPanel = new FocusPanel(outer);
129                 // Inner contains generalPanel and permPanel.
130                 inner = new VerticalPanel();
131                 inner.addStyleName("inner");
132
133         inner.add(createMainPanel());
134
135         outer.add(inner);
136
137                 final Button ok = new Button("OK", new ClickHandler() {
138                         @Override
139                         public void onClick(ClickEvent event) {
140                                 closeDialog();
141                         }
142                 });
143                 ok.addStyleName("button");
144
145         outer.add(ok);
146         outer.setCellHorizontalAlignment(inner, HasHorizontalAlignment.ALIGN_CENTER);
147
148         focusPanel.setFocus(true);
149         setWidget(outer);
150         }
151
152     private boolean IamOwner() {
153         return file.getOwnerID().equals(app.getUserID());
154     }
155
156     private void populatePublicSharingPanel(VerticalPanel publicSharingPanel) {
157         if(IamOwner()) {
158             final HorizontalPanel publicCheckPanel = new HorizontalPanel();
159             publicCheckPanel.setSpacing(8);
160
161             // Check box header
162             final CheckBox publicCheckBox = new CheckBox();
163             Label publicCheckTitle = new InlineHTML("<b>Public on the Internet</b>");
164             Label publicCheckInfo = new Label("Anyone who has the public link can access. No sign-in required.", true);
165
166             publicCheckBox.setValue(isFilePubliclyShared());
167             publicCheckBox.addClickHandler(new ClickHandler() {
168                 @Override
169                 public void onClick(ClickEvent event) {
170                     final Boolean published;
171                     if(publicCheckBox.getValue() != file.isPublished() && IamOwner()) {
172                         published = publicCheckBox.getValue();
173                     }
174                     else {
175                         published = Boolean.FALSE;
176                     }
177
178                     updateMetaDataForPublicSharing(published);
179                 }
180             });
181
182             publicCheckPanel.add(publicCheckBox);
183             publicCheckPanel.add(publicCheckTitle);
184             publicCheckPanel.add(publicCheckInfo);
185
186             // Public Link
187             publicPathPanel.setVisible(false);
188             publicPathPanel.setWidth(Const.PERCENT_100);
189             publicPathPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
190             publicPathPanel.add(new Label("Link"));
191             publicPathPanel.setSpacing(8);
192             publicPathPanel.addStyleName("pithos-TabPanelBottom");
193
194             publicPathText.setWidth(Const.PERCENT_100);
195             publicPathText.addClickHandler(new ClickHandler() {
196                 @Override
197                 public void onClick(ClickEvent event) {
198                     Pithos.enableIESelection();
199                     ((TextBox) event.getSource()).selectAll();
200                     Pithos.preventIESelection();
201                 }
202             });
203             publicPathText.setText(Window.Location.getHost() + file.getPublicUri());
204             publicPathText.setTitle("Use this link for sharing the file via e-mail, IM, etc. (crtl-C/cmd-C to copy to system clipboard)");
205             publicPathText.setReadOnly(true);
206             publicPathPanel.add(publicPathText);
207
208             publicSharingPanel.add(publicCheckPanel);
209             publicSharingPanel.add(publicPathPanel);
210
211             Scheduler.get().scheduleDeferred(new Command() {
212                 @Override
213                 public void execute() {
214                     showLinkForPublicSharing();
215                 }
216             });
217         }
218     }
219
220     private void populatePrivateSharingPanel(VerticalPanel privateSharingPanel) {
221         final HorizontalPanel privateCheckPanel = new HorizontalPanel();
222         final VerticalPanel privatePermPanel = new VerticalPanel();
223         final HorizontalPanel permButtons = new HorizontalPanel();
224
225         privateCheckPanel.setSpacing(8);
226         privatePermPanel.setVisible(isFilePrivatelyShared());
227         permButtons.setSpacing(8);
228
229         // Check box header
230         final CheckBox privateCheckBox = new CheckBox();
231         final Label privateCheckTitle = new  InlineHTML("<b>Private sharing</b>");
232         final Label privateCheckInfo = new Label("Only people explicitly granted permission can access. Sign-in required.", true);
233
234         privateCheckBox.setValue(isFilePrivatelyShared());
235         privateCheckBox.addClickHandler(new ClickHandler() {
236             @Override
237             public void onClick(ClickEvent event) {
238                 if(isFilePrivatelyShared()) {
239                     // ignore the click, set it always to "checked"
240                     privateCheckBox.setValue(true);
241                     // show permissions
242                     privatePermPanel.setVisible(true);
243                 }
244                 else {
245                     // This is the value *after* the click is applied :)
246                     boolean isChecked = privateCheckBox.getValue();
247                     privatePermPanel.setVisible(isChecked);
248                 }
249             }
250         });
251
252         privateCheckPanel.add(privateCheckBox);
253         privateCheckPanel.add(privateCheckTitle);
254         privateCheckPanel.add(privateCheckInfo);
255
256         // Permission list
257         permList = new PermissionsList(app, privateSharingImages, file.getPermissions(), file.getOwnerID(), false, new Command() {
258             @Override
259             public void execute() {
260                 updateMetaDataForPrivateSharing();
261             }
262         });
263
264         // Permission Add buttons
265         Button addUser = new Button("Add User", new ClickHandler() {
266             @Override
267             public void onClick(ClickEvent event) {
268                 PermissionsAddDialog dlg = new PermissionsAddDialog(app, app.getAccount().getGroups(), permList, true);
269                 dlg.center();
270                 permList.updatePermissionTable();
271             }
272         });
273         addUser.addStyleName("button");
274         permButtons.add(addUser);
275
276         Button addGroup = new Button("Add Group", new ClickHandler() {
277             @Override
278             public void onClick(ClickEvent event) {
279                 if (app.getAccount().getGroups().isEmpty()) {
280                     new GroupCreateDialog(app, new Command() {
281
282                         @Override
283                         public void execute() {
284                             if (app.getAccount().getGroups().isEmpty()) {
285                                 return;
286                             }
287                             PermissionsAddDialog dlg = new PermissionsAddDialog(app, app.getAccount().getGroups(), permList, false);
288                             dlg.center();
289                             permList.updatePermissionTable();
290                         }
291                     }).center();
292                 }
293                 else {
294                     PermissionsAddDialog dlg = new PermissionsAddDialog(app, app.getAccount().getGroups(), permList, false);
295                     dlg.center();
296                     permList.updatePermissionTable();
297                 }
298             }
299         });
300         addGroup.addStyleName("button");
301         permButtons.add(addGroup);
302
303
304         Button addEverybody = new Button("Add everybody", new ClickHandler() {
305             @Override
306             public void onClick(ClickEvent event) {
307                 Pithos.LOG("Adding to Everybody");
308                 Pithos.LOG("");
309                 permList.addPermission("*", true, false);
310                 permList.updatePermissionTable();
311             }
312         });
313         addEverybody.addStyleName("button");
314         permButtons.add(addEverybody);
315
316         permButtons.setCellHorizontalAlignment(addGroup, HasHorizontalAlignment.ALIGN_CENTER);
317         privatePermPanel.add(permList);
318         privatePermPanel.add(permButtons);
319
320         // Private Link
321         privatePathPanel.setVisible(false);
322         privatePathPanel.setWidth(Const.PERCENT_100);
323         privatePathPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
324         privatePathPanel.add(new Label("Link"));
325         privatePathPanel.setSpacing(8);
326         privatePathPanel.addStyleName("pithos-TabPanelBottom");
327
328         privatePathText.setWidth(Const.PERCENT_100);
329         privatePathText.addClickHandler(new ClickHandler() {
330             @Override
331             public void onClick(ClickEvent event) {
332                 Pithos.enableIESelection();
333                 ((TextBox) event.getSource()).selectAll();
334                 Pithos.preventIESelection();
335             }
336         });
337         privatePathText.setText(Window.Location.getHost() + file.getPublicUri());
338         privatePathText.setTitle("Use this link for sharing the file via e-mail, IM, etc. (crtl-C/cmd-C to copy to system clipboard)");
339         privatePathText.setWidth(Const.PERCENT_100);
340         privatePathText.setReadOnly(true);
341         privatePathPanel.add(privatePathText);
342
343         Scheduler.get().scheduleDeferred(new Command() {
344             @Override
345             public void execute() {
346                 showLinkForPrivateSharing();
347             }
348         });
349
350         privateSharingPanel.add(privateCheckPanel);
351         privateSharingPanel.add(privatePermPanel);
352         privateSharingPanel.add(privatePathPanel);
353     }
354
355     private Panel createMainPanel() {
356         VerticalPanel panelAll = new VerticalPanel();
357         VerticalPanel panelPublic = new VerticalPanel();
358         VerticalPanel panelPrivate = new VerticalPanel();
359
360         populatePrivateSharingPanel(panelPrivate);
361         populatePublicSharingPanel(panelPublic);
362
363         panelAll.add(panelPrivate);
364         panelAll.add(panelPublic);
365
366         return panelAll;
367     }
368
369     private boolean isFilePubliclyShared() {
370         return file.isPublished();
371     }
372
373     private boolean isFilePrivatelyShared() {
374         return file.isShared();
375     }
376
377     private void showLinkForPublicSharing() {
378                 if (isFilePubliclyShared()) {
379                         UrlBuilder b = Window.Location.createUrlBuilder();
380                         b.setPath(file.getPublicUri());
381                         publicPathText.setText(b.buildString());
382                 publicPathPanel.setVisible(true);
383                 }
384                 else {
385                         publicPathPanel.setVisible(false);
386                 }
387     }
388
389     private void showLinkForPrivateSharing() {
390         if (isFilePrivatelyShared()) {
391             UrlBuilder b = Window.Location.createUrlBuilder();
392             b.setPath(app.getApiPath() + file.getOwnerID() + file.getUri());
393             String href = Window.Location.getHref();
394             boolean hasParameters = href.contains(Const.QUESTION_MARK);
395             privatePathText.setText(href + (hasParameters ? Const.AMPERSAND : Const.QUESTION_MARK) + Const.GOTO_EQ + b.buildString());
396             privatePathPanel.setVisible(true);
397         }
398         else {
399             privatePathPanel.setVisible(false);
400         }
401     }
402
403         protected void updateMetaDataForPublicSharing(String api, String owner, final String path, final Boolean published) {
404         if (published != null) {
405             PostRequest updateFile = new PostRequest(api, owner, path) {
406                 @Override
407                 public void onSuccess(Resource result) {
408                         HeadRequest<File> headFile = new HeadRequest<File>(File.class, app.getApiPath(), file.getOwnerID(), path, file) {
409
410                                                 @Override
411                                                 public void onSuccess(File _result) {
412                                                         showLinkForPublicSharing();
413                                                         if (!app.isMySharedSelected()) {
414                                             app.updateFolder(file.getParent(), true, new Command() {
415                                                                         
416                                                                         @Override
417                                                                         public void execute() {
418                                                                                 app.updateMySharedRoot();
419                                                                         }
420                                                                 }, true);
421                             }
422                                                         else {
423                                                                 app.updateSharedFolder(file.getParent(), true);
424                             }
425                                                 }
426
427                                                 @Override
428                                                 public void onError(Throwable t) {
429                                                         app.setError(t);
430                                     app.displayError("System error modifying file:" + t.getMessage());
431                                                 }
432
433                                                 @Override
434                                                 protected void onUnauthorized(Response response) {
435                                                         app.sessionExpired();
436                                                 }
437                                         };
438                                         headFile.setHeader(Const.X_AUTH_TOKEN, app.getUserToken());
439                                         Scheduler.get().scheduleDeferred(headFile);
440                 }
441
442                 @Override
443                 public void onError(Throwable t) {
444                                         app.setError(t);
445                     app.displayError("System error modifying file:" + t.getMessage());
446                 }
447
448                                 @Override
449                                 protected void onUnauthorized(Response response) {
450                                         app.sessionExpired();
451                                 }
452             };
453             updateFile.setHeader(Const.X_AUTH_TOKEN, app.getUserToken());
454             updateFile.setHeader(Const.X_OBJECT_PUBLIC, published.toString());
455             Scheduler.get().scheduleDeferred(updateFile);
456         }
457         else if (!app.isMySharedSelected())
458             app.updateFolder(file.getParent(), true, new Command() {
459                                 @Override
460                                 public void execute() {
461                                         if (file.isSharedOrPublished()) {
462                         app.updateMySharedRoot();
463                     }
464                                 }
465                         }, true);
466         else
467                 app.updateSharedFolder(file.getParent(), true);
468     }
469     protected void updateMetaDataForPublicSharing(Boolean published) {
470         updateMetaDataForPublicSharing(
471             app.getApiPath(),
472             app.getUserID(),
473             file.getUri() + Const.QUESTION_MARK_UPDATE_EQ,
474             published
475         );
476     }
477
478     protected void updateMetaDataForPrivateSharing(String api, String owner, final String path, final Map<String, Boolean[]> newPermissions) {
479         if (newPermissions != null) {
480             PostRequest updateFile = new PostRequest(api, owner, path) {
481                 @Override
482                 public void onSuccess(Resource result) {
483                     HeadRequest<File> headFile = new HeadRequest<File>(File.class, app.getApiPath(), file.getOwnerID(), path, file) {
484
485                         @Override
486                         public void onSuccess(File _result) {
487                             showLinkForPrivateSharing();
488                             if (!app.isMySharedSelected())
489                                 app.updateFolder(file.getParent(), true, new Command() {
490
491                                     @Override
492                                     public void execute() {
493                                         app.updateMySharedRoot();
494                                     }
495                                 }, true);
496                             else
497                                 app.updateSharedFolder(file.getParent(), true);
498                         }
499
500                         @Override
501                         public void onError(Throwable t) {
502                             app.setError(t);
503                             app.displayError("System error modifying file:" + t.getMessage());
504                         }
505
506                         @Override
507                         protected void onUnauthorized(Response response) {
508                             app.sessionExpired();
509                         }
510                     };
511                     headFile.setHeader(Const.X_AUTH_TOKEN, app.getUserToken());
512                     Scheduler.get().scheduleDeferred(headFile);
513                 }
514
515                 @Override
516                 public void onError(Throwable t) {
517                     app.setError(t);
518                     app.displayError("System error modifying file:" + t.getMessage());
519                 }
520
521                 @Override
522                 protected void onUnauthorized(Response response) {
523                     app.sessionExpired();
524                 }
525             };
526             updateFile.setHeader(Const.X_AUTH_TOKEN, app.getUserToken());
527
528             String readPermHeader = Const.READ_EQ;
529             String writePermHeader = Const.WRITE_EQ;
530             for (String u : newPermissions.keySet()) {
531                 Boolean[] p = newPermissions.get(u);
532                 if (p[0] != null && p[0]) {
533                     readPermHeader += u + Const.COMMA;
534                 }
535                 if (p[1] != null && p[1]) {
536                     writePermHeader += u + Const.COMMA;
537                 }
538             }
539             if (readPermHeader.endsWith(Const.EQ)) {
540                 readPermHeader = "";
541             }
542             else if (readPermHeader.endsWith(Const.COMMA)) {
543                 readPermHeader = readPermHeader.substring(0, readPermHeader.length() - 1);
544             }
545             if (writePermHeader.endsWith(Const.EQ)) {
546                 writePermHeader = "";
547             }
548             else if (writePermHeader.endsWith(Const.COMMA)) {
549                 writePermHeader = writePermHeader.substring(0, writePermHeader.length() - 1);
550             }
551             String permHeader = readPermHeader +
552                 ((readPermHeader.length()  > 0 && writePermHeader.length() > 0) ?  Const.SEMI : "") + writePermHeader;
553             if (permHeader.length() == 0) {
554                 permHeader=Const.TILDE;
555             }
556             else {
557                 permHeader = URL.encodePathSegment(permHeader);
558             }
559             updateFile.setHeader(Const.X_OBJECT_SHARING, permHeader);
560             Scheduler.get().scheduleDeferred(updateFile);
561         }
562         else if (!app.isMySharedSelected()) {
563             app.updateFolder(file.getParent(), true, new Command() {
564                 @Override
565                 public void execute() {
566                     if (file.isSharedOrPublished())
567                         app.updateMySharedRoot();
568                 }
569             }, true);
570         }
571         else {
572             app.updateSharedFolder(file.getParent(), true);
573         }
574     }
575     protected void updateMetaDataForPrivateSharing() {
576         updateMetaDataForPrivateSharing(
577             app.getApiPath(),
578             app.getUserID(),
579             file.getUri() + Const.QUESTION_MARK_UPDATE_EQ,
580             permList.getPermissions()
581         );
582     }
583     @Override
584         protected void onPreviewNativeEvent(NativePreviewEvent preview) {
585             super.onPreviewNativeEvent(preview);
586
587             NativeEvent evt = preview.getNativeEvent();
588             if (evt.getType().equals(Const.EVENT_TYPE_KEYDOWN) &&
589             evt.getKeyCode() == KeyCodes.KEY_ENTER) {
590
591             closeDialog();
592         }
593         }
594
595     @Override
596     protected boolean accept() {
597         return true;
598     }
599 }