Added meta data to file properties dialog. Still unstyled
[pithos-web-client] / src / gr / grnet / pithos / web / client / FilePropertiesDialog.java
1 /*\r
2  * Copyright 2011 GRNET S.A. All rights reserved.\r
3  *\r
4  * Redistribution and use in source and binary forms, with or\r
5  * without modification, are permitted provided that the following\r
6  * conditions are met:\r
7  *\r
8  *   1. Redistributions of source code must retain the above\r
9  *      copyright notice, this list of conditions and the following\r
10  *      disclaimer.\r
11  *\r
12  *   2. Redistributions in binary form must reproduce the above\r
13  *      copyright notice, this list of conditions and the following\r
14  *      disclaimer in the documentation and/or other materials\r
15  *      provided with the distribution.\r
16  *\r
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
28  * POSSIBILITY OF SUCH DAMAGE.\r
29  *\r
30  * The views and conclusions contained in the software and\r
31  * documentation are those of the authors and should not be\r
32  * interpreted as representing official policies, either expressed\r
33  * or implied, of GRNET S.A.\r
34  */\r
35 package gr.grnet.pithos.web.client;\r
36 \r
37 import gr.grnet.pithos.web.client.foldertree.File;\r
38 import gr.grnet.pithos.web.client.foldertree.FileVersions;\r
39 import gr.grnet.pithos.web.client.foldertree.Resource;\r
40 import gr.grnet.pithos.web.client.foldertree.Version;\r
41 import gr.grnet.pithos.web.client.rest.GetRequest;\r
42 import gr.grnet.pithos.web.client.rest.PostRequest;\r
43 import gr.grnet.pithos.web.client.rest.PutRequest;\r
44 import gr.grnet.pithos.web.client.rest.RestException;\r
45 \r
46 import java.util.HashMap;\r
47 import java.util.List;\r
48 import java.util.Map;\r
49 \r
50 import org.apache.commons.collections.map.HashedMap;\r
51 \r
52 import com.google.gwt.core.client.GWT;\r
53 import com.google.gwt.core.client.Scheduler;\r
54 import com.google.gwt.event.dom.client.ClickEvent;\r
55 import com.google.gwt.event.dom.client.ClickHandler;\r
56 import com.google.gwt.http.client.Response;\r
57 import com.google.gwt.http.client.URL;\r
58 import com.google.gwt.i18n.client.DateTimeFormat;\r
59 import com.google.gwt.resources.client.ImageResource;\r
60 import com.google.gwt.user.client.Window;\r
61 import com.google.gwt.user.client.ui.Anchor;\r
62 import com.google.gwt.user.client.ui.Button;\r
63 import com.google.gwt.user.client.ui.CheckBox;\r
64 import com.google.gwt.user.client.ui.DecoratedTabPanel;\r
65 import com.google.gwt.user.client.ui.FlexTable;\r
66 import com.google.gwt.user.client.ui.FocusPanel;\r
67 import com.google.gwt.user.client.ui.HasHorizontalAlignment;\r
68 import com.google.gwt.user.client.ui.HorizontalPanel;\r
69 import com.google.gwt.user.client.ui.Image;\r
70 import com.google.gwt.user.client.ui.Label;\r
71 import com.google.gwt.user.client.ui.TextBox;\r
72 import com.google.gwt.user.client.ui.VerticalPanel;\r
73 \r
74 /**\r
75  * The 'File properties' dialog box implementation.\r
76  *\r
77  */\r
78 public class FilePropertiesDialog extends AbstractPropertiesDialog {\r
79 \r
80         protected PermissionsList permList;\r
81 \r
82         protected CheckBox readForAll;\r
83 \r
84         /**\r
85          * An image bundle for this widgets images.\r
86          */\r
87         public interface Images extends MessagePanel.Images {\r
88 \r
89                 @Source("gr/grnet/pithos/resources/edit_user.png")\r
90                 ImageResource permUser();\r
91 \r
92                 @Source("gr/grnet/pithos/resources/groups22.png")\r
93                 ImageResource permGroup();\r
94 \r
95                 @Source("gr/grnet/pithos/resources/editdelete.png")\r
96                 ImageResource delete();\r
97 \r
98                 @Source("gr/grnet/pithos/resources/db_update.png")\r
99                 ImageResource restore();\r
100 \r
101                 @Source("gr/grnet/pithos/resources/folder_inbox.png")\r
102                 ImageResource download();\r
103         }\r
104 \r
105         /**\r
106          * The widget that holds the name of the file.\r
107          */\r
108         private TextBox name = new TextBox();\r
109 \r
110         final File file;\r
111 \r
112     Images images = GWT.create(Images.class);\r
113 \r
114     private FlexTable metaTable;\r
115         /**\r
116          * The widget's constructor.\r
117          */\r
118         public FilePropertiesDialog(Pithos _app, File _file) {\r
119         super(_app);\r
120         file = _file;\r
121 \r
122                 Anchor close = new Anchor();\r
123                 close.addStyleName("close");\r
124                 close.addClickHandler(new ClickHandler() {\r
125                         \r
126                         @Override\r
127                         public void onClick(ClickEvent event) {\r
128                                 hide();\r
129                         }\r
130                 });\r
131                 // Set the dialog's caption.\r
132                 setText("File properties");\r
133                 setAnimationEnabled(true);\r
134                 setGlassEnabled(true);\r
135                 setStyleName("pithos-DialogBox");\r
136 \r
137                 // Outer contains inner and buttons.\r
138                 final VerticalPanel outer = new VerticalPanel();\r
139                 outer.add(close);\r
140                 final FocusPanel focusPanel = new FocusPanel(outer);\r
141                 // Inner contains generalPanel and permPanel.\r
142                 inner = new DecoratedTabPanel();\r
143                 inner.setAnimationEnabled(true);\r
144                 inner.addStyleName("inner");\r
145                 inner.getDeckPanel().addStyleName("pithos-TabPanelBottom");\r
146 \r
147 \r
148         inner.add(createGeneralPanel(), "General");\r
149 \r
150         inner.add(createSharingPanel(), "Sharing");\r
151 \r
152                 fetchVersions();\r
153                         \r
154         inner.selectTab(0);\r
155 \r
156         outer.add(inner);\r
157 \r
158                 // Create the 'OK' button, along with a listener that hides the dialog\r
159                 // when the button is clicked.\r
160                 final Button ok = new Button("OK", new ClickHandler() {\r
161                         @Override\r
162                         public void onClick(@SuppressWarnings("unused") ClickEvent event) {\r
163                                 accept();\r
164                                 closeDialog();\r
165                         }\r
166                 });\r
167                 ok.addStyleName("button");\r
168 \r
169         outer.add(ok);\r
170         outer.setCellHorizontalAlignment(inner, HasHorizontalAlignment.ALIGN_CENTER);\r
171 \r
172         focusPanel.setFocus(true);\r
173         setWidget(outer);\r
174         }\r
175 \r
176     protected void fetchVersions() {\r
177         String path = file.getUri() + "?format=json&version=list";\r
178         GetRequest<FileVersions> getVersions = new GetRequest<FileVersions>(FileVersions.class, app.getApiPath(), file.getOwner(), path) {\r
179 \r
180                         @Override\r
181                         public void onSuccess(FileVersions _result) {\r
182                         inner.add(createVersionPanel(_result.getVersions()), "Versions");\r
183                         }\r
184 \r
185                         @Override\r
186                         public void onError(Throwable t) {\r
187                                 GWT.log("", t);\r
188                 if (t instanceof RestException) {\r
189                     app.displayError("Unable to fetch versions: " + ((RestException) t).getHttpStatusText());\r
190                 }\r
191                 else\r
192                     app.displayError("System error unable to fetch versions: "+t.getMessage());\r
193                         }\r
194 \r
195                         @Override\r
196                         protected void onUnauthorized(Response response) {\r
197                                 app.sessionExpired();\r
198                         }\r
199                 };\r
200                 getVersions.setHeader("X-Auth-Token", app.getToken());\r
201                 Scheduler.get().scheduleDeferred(getVersions);\r
202         }\r
203 \r
204         private VerticalPanel createGeneralPanel() {\r
205         final VerticalPanel generalPanel = new VerticalPanel();\r
206         final FlexTable generalTable = new FlexTable();\r
207         generalTable.setText(0, 0, "Name");\r
208         generalTable.setText(1, 0, "Folder");\r
209         generalTable.setText(2, 0, "Owner");\r
210         generalTable.setText(3, 0, "Last modified");\r
211 \r
212         name.setWidth("100%");\r
213         name.setText(file.getName());\r
214         generalTable.setWidget(0, 1, name);\r
215         if(file.getParent() != null)\r
216             generalTable.setText(1, 1, file.getParent().getName());\r
217         else\r
218             generalTable.setText(1, 1, "-");\r
219         generalTable.setText(2, 1, file.getOwner());\r
220 \r
221         final DateTimeFormat formatter = DateTimeFormat.getFormat("d/M/yyyy h:mm a");\r
222         generalTable.setText(3, 1, file.getLastModified() != null ? formatter.format(file.getLastModified()) : "");\r
223 \r
224         generalTable.getFlexCellFormatter().setStyleName(0, 0, "props-labels");\r
225         generalTable.getFlexCellFormatter().setStyleName(1, 0, "props-labels");\r
226         generalTable.getFlexCellFormatter().setStyleName(2, 0, "props-labels");\r
227         generalTable.getFlexCellFormatter().setStyleName(3, 0, "props-labels");\r
228         generalTable.getFlexCellFormatter().setStyleName(4, 0, "props-labels");\r
229         generalTable.getFlexCellFormatter().setStyleName(0, 1, "props-values");\r
230         generalTable.getFlexCellFormatter().setStyleName(1, 1, "props-values");\r
231         generalTable.getFlexCellFormatter().setStyleName(2, 1, "props-values");\r
232         generalTable.getFlexCellFormatter().setStyleName(3, 1, "props-values");\r
233         generalTable.setCellSpacing(4);\r
234 \r
235         generalPanel.add(generalTable);\r
236 \r
237         Label meta = new Label("Meta data");\r
238         generalPanel.add(meta);\r
239                 Image plus = new Image("images/plus.png");\r
240                 plus.addStyleName("pithos-addInvitationImg");\r
241                 generalPanel.add(plus);\r
242                 \r
243                 metaTable = new FlexTable();\r
244                 metaTable.setCellSpacing(0);\r
245                 metaTable.setHTML(0, 0, "Name");\r
246                 metaTable.getFlexCellFormatter().setStyleName(0, 0, "props-labels");\r
247                 metaTable.setText(0, 1, "Value");\r
248                 metaTable.getFlexCellFormatter().setStyleName(0, 1, "props-labels");\r
249                 int rows = 1;\r
250                 for (String metaKey : file.getMeta().keySet()) {\r
251                         addFormLine(metaTable, rows++, metaKey, file.getMeta().get(metaKey));\r
252                 }\r
253                 \r
254                 plus.addClickHandler(new ClickHandler() {\r
255                         \r
256                         @Override\r
257                         public void onClick(ClickEvent event) {\r
258                                 addFormLine(metaTable, metaTable.getRowCount(), "", "");\r
259                         }\r
260                 });\r
261 \r
262                 generalPanel.add(metaTable);\r
263         generalPanel.setSpacing(4);\r
264         return generalPanel;\r
265     }\r
266 \r
267         private void addFormLine(final FlexTable table, int row, String name, String value) {\r
268                 TextBox nameBox = new TextBox();\r
269                 nameBox.setText(name);\r
270                 table.setWidget(row, 0, nameBox);\r
271                 table.getFlexCellFormatter().setStyleName(1, 0, "props-values");\r
272 \r
273                 TextBox valueBox = new TextBox();\r
274                 valueBox.setText(value);\r
275                 table.setWidget(row, 1, valueBox);\r
276                 table.getFlexCellFormatter().setStyleName(1, 1, "props-values");\r
277                 \r
278                 Image delete = new Image("images/delete.png");\r
279                 delete.addStyleName("pithos-invitationDeleteImg");\r
280                 delete.addClickHandler(new ClickHandler() {\r
281                         \r
282                         @Override\r
283                         public void onClick(ClickEvent event) {\r
284                                 int rowIndex = table.getCellForEvent(event).getRowIndex();\r
285                                 table.removeRow(rowIndex);\r
286                         }\r
287                 });\r
288                 table.setWidget(row, 2, delete);\r
289         }\r
290 \r
291     private VerticalPanel createSharingPanel() {\r
292         VerticalPanel permPanel = new VerticalPanel();\r
293 \r
294         permList = new PermissionsList(images, file.getPermissions(), file.getOwner(), file.getInheritedPermissionsFrom() != null);\r
295         permPanel.add(permList);\r
296 \r
297         if (file.getInheritedPermissionsFrom() == null) {\r
298             HorizontalPanel permButtons = new HorizontalPanel();\r
299             Button add = new Button("Add Group", new ClickHandler() {\r
300                 @Override\r
301                 public void onClick(@SuppressWarnings("unused") ClickEvent event) {\r
302                     PermissionsAddDialog dlg = new PermissionsAddDialog(app, app.getAccount().getGroups(), permList, false);\r
303                     dlg.center();\r
304                     permList.updatePermissionTable();\r
305                 }\r
306             });\r
307             add.addStyleName("button");\r
308             permButtons.add(add);\r
309             permButtons.setCellHorizontalAlignment(add, HasHorizontalAlignment.ALIGN_CENTER);\r
310 \r
311             final Button addUser = new Button("Add User", new ClickHandler() {\r
312                 @Override\r
313                 public void onClick(@SuppressWarnings("unused") ClickEvent event) {\r
314                     PermissionsAddDialog dlg = new PermissionsAddDialog(app, app.getAccount().getGroups(), permList, true);\r
315                     dlg.center();\r
316                     permList.updatePermissionTable();\r
317                 }\r
318             });\r
319             addUser.addStyleName("button");\r
320             permButtons.add(addUser);\r
321             permButtons.setCellHorizontalAlignment(addUser, HasHorizontalAlignment.ALIGN_CENTER);\r
322 \r
323             permButtons.setSpacing(8);\r
324             permButtons.addStyleName("pithos-TabPanelBottom");\r
325             permPanel.add(permButtons);\r
326         }\r
327 \r
328         final Label readForAllNote = new Label("When this option is enabled, the file will be readable" +\r
329                     " by everyone. By checking this option, you are certifying that you have the right to " +\r
330                     "distribute this file and that it does not violate the Terms of Use.", true);\r
331         readForAllNote.setVisible(false);\r
332         readForAllNote.setStylePrimaryName("pithos-readForAllNote");\r
333 \r
334         readForAll = new CheckBox();\r
335         readForAll.setValue(file.isPublished());\r
336         readForAll.addClickHandler(new ClickHandler() {\r
337             @Override\r
338             public void onClick(@SuppressWarnings("unused") ClickEvent event) {\r
339                 readForAllNote.setVisible(readForAll.getValue());\r
340             }\r
341         });\r
342 \r
343         // Only show the read for all permission if the user is the owner.\r
344         if (file.getOwner().equals(app.getUsername())) {\r
345             final HorizontalPanel permForAll = new HorizontalPanel();\r
346             permForAll.add(new Label("Public"));\r
347             permForAll.add(readForAll);\r
348             permForAll.setSpacing(8);\r
349             permForAll.addStyleName("pithos-TabPanelBottom");\r
350             permForAll.add(readForAllNote);\r
351             permPanel.add(permForAll);\r
352         }\r
353 \r
354         if (file.isPublished()) {\r
355             final HorizontalPanel pathPanel = new HorizontalPanel();\r
356             pathPanel.setWidth("100%");\r
357             pathPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);\r
358             pathPanel.add(new Label("Link"));\r
359             pathPanel.setSpacing(8);\r
360             pathPanel.addStyleName("pithos-TabPanelBottom");\r
361 \r
362             TextBox path = new TextBox();\r
363             path.setWidth("100%");\r
364             path.addClickHandler(new ClickHandler() {\r
365                 @Override\r
366                 public void onClick(ClickEvent event) {\r
367                     Pithos.enableIESelection();\r
368                     ((TextBox) event.getSource()).selectAll();\r
369                     Pithos.preventIESelection();\r
370                 }\r
371             });\r
372             path.setText(Window.Location.getHost() + file.getPublicUri());\r
373             path.setTitle("Use this link for sharing the file via e-mail, IM, etc. (crtl-C/cmd-C to copy to system clipboard)");\r
374             path.setWidth("100%");\r
375             path.setReadOnly(true);\r
376             pathPanel.add(path);\r
377             permPanel.add(pathPanel);\r
378         }\r
379 \r
380         return permPanel;\r
381     }\r
382 \r
383     VerticalPanel createVersionPanel(List<Version> versions) {\r
384         VerticalPanel versionPanel = new VerticalPanel();\r
385         VersionsList verList = new VersionsList(app, this, images, file, versions);\r
386         versionPanel.add(verList);\r
387         return versionPanel;\r
388     }\r
389 \r
390         /**\r
391          * Accepts any change and updates the file\r
392          *\r
393          */\r
394         @Override\r
395         protected void accept() {\r
396                 String newFilename = null;\r
397 \r
398                 final Map<String, Boolean[]> perms = (permList.hasChanges() ? permList.getPermissions() : null);\r
399 \r
400                 if (!name.getText().trim().equals(file.getName())) {\r
401                         newFilename = name.getText().trim();\r
402                 }\r
403 \r
404                 //only update the read for all perm if the user is the owner\r
405         Boolean published = null;\r
406                 if (readForAll.getValue() != file.isPublished())\r
407                         if (file.getOwner().equals(app.getUsername()))\r
408                 published = readForAll.getValue();\r
409         final Boolean finalPublished = published;\r
410 \r
411         final Map<String, String> newMeta = new HashMap<String, String>();\r
412         for (int row = 1; row < metaTable.getRowCount(); row++) {\r
413                 String key = ((TextBox) metaTable.getWidget(row, 0)).getText().trim();\r
414                 String value = ((TextBox) metaTable.getWidget(row, 1)).getText().trim();\r
415                 newMeta.put(key, value);\r
416         }\r
417 \r
418         if (newFilename != null) {\r
419             final String path = file.getParent().getUri() + "/" + newFilename;\r
420             PutRequest updateFile = new PutRequest(app.getApiPath(), app.getUsername(), path) {\r
421                 @Override\r
422                 public void onSuccess(@SuppressWarnings("unused") Resource result) {\r
423                     updateMetaData(app.getApiPath(), file.getOwner(), path + "?update=", newMeta, finalPublished, perms);\r
424                 }\r
425 \r
426                 @Override\r
427                 public void onError(Throwable t) {\r
428                     GWT.log("", t);\r
429                     app.displayError("System error modifying file:" + t.getMessage());\r
430                 }\r
431 \r
432                                 @Override\r
433                                 protected void onUnauthorized(Response response) {\r
434                                         app.sessionExpired();\r
435                                 }\r
436             };\r
437             updateFile.setHeader("X-Auth-Token", app.getToken());\r
438             updateFile.setHeader("X-Move-From", URL.encodePathSegment(file.getUri()));\r
439             updateFile.setHeader("Content-Type", file.getContentType());\r
440             Scheduler.get().scheduleDeferred(updateFile);\r
441         }\r
442         else\r
443             updateMetaData(app.getApiPath(), app.getUsername(), file.getUri() + "?update=", newMeta, finalPublished, perms);\r
444         }\r
445 \r
446         protected void updateMetaData(String api, String owner, String path, Map<String, String> newMeta, Boolean published, Map<String, Boolean[]> newPermissions) {\r
447         if (newMeta != null || published != null || newPermissions != null) {\r
448             PostRequest updateFile = new PostRequest(api, owner, path) {\r
449                 @Override\r
450                 public void onSuccess(@SuppressWarnings("unused") Resource result) {\r
451                     app.updateFolder(file.getParent(), true, null);\r
452                 }\r
453 \r
454                 @Override\r
455                 public void onError(Throwable t) {\r
456                     GWT.log("", t);\r
457                     app.displayError("System error modifying file:" + t.getMessage());\r
458                 }\r
459 \r
460                                 @Override\r
461                                 protected void onUnauthorized(Response response) {\r
462                                         app.sessionExpired();\r
463                                 }\r
464             };\r
465             updateFile.setHeader("X-Auth-Token", app.getToken());\r
466             for (String t : file.getMeta().keySet()) {\r
467                         updateFile.setHeader("X-Object-Meta-" + URL.encodePathSegment(t.trim()), "~");\r
468             }\r
469             for (String key : newMeta.keySet())\r
470                 updateFile.setHeader("X-Object-Meta-" + URL.encodePathSegment(key.trim()), URL.encodePathSegment(newMeta.get(key)));\r
471             \r
472             if (published != null)\r
473                 updateFile.setHeader("X-Object-Public", published.toString());\r
474             if (newPermissions != null) {\r
475                 String readPermHeader = "read=";\r
476                 String writePermHeader = "write=";\r
477                 for (String u : newPermissions.keySet()) {\r
478                     Boolean[] p = newPermissions.get(u);\r
479                     if (p[0] != null && p[0])\r
480                         readPermHeader += u + ",";\r
481                     if (p[1] != null && p[1])\r
482                         writePermHeader += u + ",";\r
483                 }\r
484                 if (readPermHeader.endsWith("="))\r
485                     readPermHeader = "";\r
486                 else if (readPermHeader.endsWith(","))\r
487                     readPermHeader = readPermHeader.substring(0, readPermHeader.length() - 1);\r
488                 if (writePermHeader.endsWith("="))\r
489                     writePermHeader = "";\r
490                 else if (writePermHeader.endsWith(","))\r
491                     writePermHeader = writePermHeader.substring(0, writePermHeader.length() - 1);\r
492                 String permHeader = readPermHeader +  ((readPermHeader.length()  > 0 && writePermHeader.length() > 0) ?  ";" : "") + writePermHeader;\r
493                 if (permHeader.length() == 0)\r
494                     permHeader="~";\r
495                 else\r
496                         permHeader = URL.encodePathSegment(permHeader);\r
497                 updateFile.setHeader("X-Object-Sharing", permHeader);\r
498             }\r
499             Scheduler.get().scheduleDeferred(updateFile);\r
500         }\r
501         else\r
502             app.updateFolder(file.getParent(), true, null);\r
503     }\r
504 }\r