1 package org.argeo.cms.ui.fs;
2
3 import java.io.IOException;
4 import java.net.URI;
5 import java.net.URISyntaxException;
6 import java.nio.file.DirectoryStream;
7 import java.nio.file.FileSystem;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10 import java.nio.file.Paths;
11 import java.nio.file.spi.FileSystemProvider;
12 import java.security.PrivilegedActionException;
13 import java.security.PrivilegedExceptionAction;
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import javax.jcr.Node;
18 import javax.jcr.Repository;
19 import javax.jcr.Session;
20
21 import org.argeo.api.NodeUtils;
22 import org.argeo.cms.CmsException;
23 import org.argeo.cms.auth.CurrentUser;
24 import org.argeo.cms.ui.util.CmsUiUtils;
25 import org.argeo.eclipse.ui.ColumnDefinition;
26 import org.argeo.eclipse.ui.EclipseUiUtils;
27 import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider;
28 import org.argeo.eclipse.ui.fs.FsTableViewer;
29 import org.argeo.eclipse.ui.fs.FsUiConstants;
30 import org.argeo.eclipse.ui.fs.FsUiUtils;
31 import org.argeo.eclipse.ui.fs.NioFileLabelProvider;
32 import org.argeo.jcr.JcrUtils;
33 import org.eclipse.jface.viewers.DoubleClickEvent;
34 import org.eclipse.jface.viewers.IDoubleClickListener;
35 import org.eclipse.jface.viewers.ISelectionChangedListener;
36 import org.eclipse.jface.viewers.IStructuredSelection;
37 import org.eclipse.jface.viewers.SelectionChangedEvent;
38 import org.eclipse.jface.viewers.Viewer;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.custom.SashForm;
41 import org.eclipse.swt.events.KeyEvent;
42 import org.eclipse.swt.events.KeyListener;
43 import org.eclipse.swt.events.ModifyEvent;
44 import org.eclipse.swt.events.ModifyListener;
45 import org.eclipse.swt.events.MouseAdapter;
46 import org.eclipse.swt.events.MouseEvent;
47 import org.eclipse.swt.events.SelectionAdapter;
48 import org.eclipse.swt.events.SelectionEvent;
49 import org.eclipse.swt.graphics.Point;
50 import org.eclipse.swt.layout.GridData;
51 import org.eclipse.swt.layout.GridLayout;
52 import org.eclipse.swt.layout.RowData;
53 import org.eclipse.swt.layout.RowLayout;
54 import org.eclipse.swt.widgets.Button;
55 import org.eclipse.swt.widgets.Composite;
56 import org.eclipse.swt.widgets.Control;
57 import org.eclipse.swt.widgets.Label;
58 import org.eclipse.swt.widgets.Table;
59 import org.eclipse.swt.widgets.Text;
60
61
62
63
64
65 public class CmsFsBrowser extends Composite {
66
67 private static final long serialVersionUID = -40347919096946585L;
68
69 private final FileSystemProvider nodeFileSystemProvider;
70 private final Node currentBaseContext;
71
72
73 private Composite leftPannelCmp;
74 private Composite filterCmp;
75 private Text filterTxt;
76 private FsTableViewer directoryDisplayViewer;
77 private Composite rightPannelCmp;
78
79 private FsContextMenu contextMenu;
80
81
82 private Path initialPath;
83 private Path currDisplayedFolder;
84 private Path currSelected;
85
86
87 private int bookmarkColWith = 500;
88
89
90
91
92
93
94 private final static String NODE_PREFIX = "node://";
95
96 private String getCurrentHomePath() {
97 Session session = null;
98 try {
99 Repository repo = currentBaseContext.getSession().getRepository();
100 session = CurrentUser.tryAs(() -> repo.login());
101 String homepath = NodeUtils.getUserHome(session).getPath();
102 return homepath;
103 } catch (Exception e) {
104 throw new CmsException("Cannot retrieve Current User Home Path", e);
105 } finally {
106 JcrUtils.logoutQuietly(session);
107 }
108 }
109
110 protected Path[] getMyFilesPath() {
111
112 String currHomeUriStr = NODE_PREFIX + getCurrentHomePath();
113 try {
114 URI uri = new URI(currHomeUriStr);
115 FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri);
116 if (fileSystem == null) {
117 PrivilegedExceptionAction<FileSystem> pea = new PrivilegedExceptionAction<FileSystem>() {
118 @Override
119 public FileSystem run() throws Exception {
120 return nodeFileSystemProvider.newFileSystem(uri, null);
121 }
122
123 };
124 fileSystem = CurrentUser.tryAs(pea);
125 }
126 Path[] paths = { fileSystem.getPath(getCurrentHomePath()), fileSystem.getPath("/") };
127 return paths;
128 } catch (URISyntaxException | PrivilegedActionException e) {
129 throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e);
130 }
131 }
132
133 private Path[] getMyGroupsFilesPath() {
134
135 Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") };
136 return paths;
137 }
138
139 private Path[] getMyBookmarks() {
140
141 Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") };
142 return paths;
143 }
144
145
146
147 public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) {
148 super(parent, style);
149 this.nodeFileSystemProvider = fileSystemProvider;
150 this.currentBaseContext = context;
151
152 this.setLayout(EclipseUiUtils.noSpaceGridLayout());
153
154 SashForm form = new SashForm(this, SWT.HORIZONTAL);
155
156 leftPannelCmp = new Composite(form, SWT.NO_FOCUS);
157
158 populateBookmarks(leftPannelCmp);
159
160 Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS);
161 createDisplay(centerCmp);
162
163 rightPannelCmp = new Composite(form, SWT.NO_FOCUS);
164
165 form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
166 form.setWeights(new int[] { 15, 40, 20 });
167 }
168
169 void refresh() {
170 modifyFilter(false);
171
172 }
173
174 private void createDisplay(final Composite parent) {
175 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
176
177
178 filterCmp = new Composite(parent, SWT.NO_FOCUS);
179 filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
180 addFilterPanel(filterCmp);
181
182
183 directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
184 List<ColumnDefinition> colDefs = new ArrayList<>();
185 colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250));
186 colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100));
187 colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150));
188 colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
189 "Last modified", 400));
190 final Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
191 table.setLayoutData(EclipseUiUtils.fillAll());
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() {
225
226 @Override
227 public void selectionChanged(SelectionChangedEvent event) {
228 IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
229 Path selected = null;
230 if (selection.isEmpty())
231 setSelected(null);
232 else
233 selected = ((Path) selection.getFirstElement());
234 if (selected != null) {
235
236 setSelected(selected);
237 }
238 }
239 });
240
241 directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
242 @Override
243 public void doubleClick(DoubleClickEvent event) {
244 IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
245 Path selected = null;
246 if (!selection.isEmpty())
247 selected = ((Path) selection.getFirstElement());
248 if (selected != null) {
249 if (!Files.isDirectory(selected))
250 return;
251 setInput(selected);
252 }
253 }
254 });
255
256
257 contextMenu = new FsContextMenu(this);
258
259 table.addMouseListener(new MouseAdapter() {
260 private static final long serialVersionUID = 6737579410648595940L;
261
262 @Override
263 public void mouseDown(MouseEvent e) {
264 if (e.button == 3) {
265
266 contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder);
267 }
268 }
269 });
270 }
271
272 private void addPathElementBtn(Path path) {
273 Button elemBtn = new Button(filterCmp, SWT.PUSH);
274 String nameStr;
275 if (path.toString().equals("/"))
276 nameStr = "[jcr:root]";
277 else
278 nameStr = path.getFileName().toString();
279 elemBtn.setText(nameStr + " >> ");
280 CmsUiUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN);
281 elemBtn.addSelectionListener(new SelectionAdapter() {
282 private static final long serialVersionUID = -4103695476023480651L;
283
284 @Override
285 public void widgetSelected(SelectionEvent e) {
286 setInput(path);
287 }
288 });
289 }
290
291 public void setInput(Path path) {
292 if (path.equals(currDisplayedFolder))
293 return;
294 currDisplayedFolder = path;
295
296 Path diff = initialPath.relativize(currDisplayedFolder);
297
298 for (Control child : filterCmp.getChildren())
299 if (!child.equals(filterTxt))
300 child.dispose();
301
302 addPathElementBtn(initialPath);
303 Path currTarget = initialPath;
304 if (!diff.toString().equals(""))
305 for (Path pathElem : diff) {
306 currTarget = currTarget.resolve(pathElem);
307 addPathElementBtn(currTarget);
308 }
309
310 filterTxt.setText("");
311 filterTxt.moveBelow(null);
312 setSelected(null);
313 filterCmp.getParent().layout(true, true);
314 }
315
316 private void setSelected(Path path) {
317 currSelected = path;
318 setOverviewInput(path);
319 }
320
321 public Viewer getViewer() {
322 return directoryDisplayViewer;
323 }
324
325 private void populateBookmarks(Composite parent) {
326 CmsUiUtils.clear(parent);
327 parent.setLayout(new GridLayout());
328 ISelectionChangedListener selList = new BookmarksSelChangeListener();
329
330 FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL);
331 Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith);
332 GridData gd = EclipseUiUtils.fillWidth();
333 gd.horizontalIndent = 10;
334 table.setLayoutData(gd);
335 homeViewer.addSelectionChangedListener(selList);
336 homeViewer.setPathsInput(getMyFilesPath());
337
338 appendTitle(parent, "Shared files");
339 FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL);
340 table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith);
341 gd = EclipseUiUtils.fillWidth();
342 gd.horizontalIndent = 10;
343 table.setLayoutData(gd);
344 groupsViewer.addSelectionChangedListener(selList);
345 groupsViewer.setPathsInput(getMyGroupsFilesPath());
346
347 appendTitle(parent, "My bookmarks");
348 FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL);
349 table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith);
350 gd = EclipseUiUtils.fillWidth();
351 gd.horizontalIndent = 10;
352 table.setLayoutData(gd);
353 bookmarksViewer.addSelectionChangedListener(selList);
354 bookmarksViewer.setPathsInput(getMyBookmarks());
355 }
356
357
358
359
360
361 private void setOverviewInput(Path path) {
362 try {
363 EclipseUiUtils.clear(rightPannelCmp);
364 rightPannelCmp.setLayout(new GridLayout());
365 if (path != null) {
366
367
368
369
370
371
372
373
374 Label contextL = new Label(rightPannelCmp, SWT.NONE);
375 contextL.setText(path.getFileName().toString());
376 contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp));
377 addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString());
378
379
380 if (Files.isDirectory(path)) {
381 addProperty(rightPannelCmp, "Type", "Folder");
382 } else {
383 String mimeType = Files.probeContentType(path);
384 if (EclipseUiUtils.isEmpty(mimeType))
385 mimeType = "<i>Unknown</i>";
386 addProperty(rightPannelCmp, "Type", mimeType);
387 addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false));
388 }
389 }
390 rightPannelCmp.layout(true, true);
391 } catch (IOException e) {
392 throw new CmsException("Cannot display details for " + path.toString(), e);
393 }
394 }
395
396 private void addFilterPanel(Composite parent) {
397 RowLayout rl = new RowLayout(SWT.HORIZONTAL);
398 rl.wrap = true;
399 parent.setLayout(rl);
400
401
402
403 filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL);
404 filterTxt.setMessage("Search current folder");
405 filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT));
406 filterTxt.addModifyListener(new ModifyListener() {
407 private static final long serialVersionUID = 1L;
408
409 public void modifyText(ModifyEvent event) {
410 modifyFilter(false);
411 }
412 });
413 filterTxt.addKeyListener(new KeyListener() {
414 private static final long serialVersionUID = 2533535233583035527L;
415
416 @Override
417 public void keyReleased(KeyEvent e) {
418 }
419
420 @Override
421 public void keyPressed(KeyEvent e) {
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457 }
458 });
459 }
460
461 private Path getOnlyChild(Path parent, String filter) {
462 try (DirectoryStream<Path> stream = Files.newDirectoryStream(currDisplayedFolder, filter + "*")) {
463 Path uniqueChild = null;
464 boolean moreThanOne = false;
465 loop: for (Path entry : stream) {
466 if (uniqueChild == null) {
467 uniqueChild = entry;
468 } else {
469 moreThanOne = true;
470 break loop;
471 }
472 }
473 if (!moreThanOne)
474 return uniqueChild;
475 return null;
476 } catch (IOException ioe) {
477 throw new CmsException(
478 "Unable to determine unique child existence and get it under " + parent + " with filter " + filter,
479 ioe);
480 }
481 }
482
483 private void modifyFilter(boolean fromOutside) {
484 if (!fromOutside)
485 if (currDisplayedFolder != null) {
486 String filter = filterTxt.getText() + "*";
487 directoryDisplayViewer.setInput(currDisplayedFolder, filter);
488 }
489 }
490
491 private class BookmarksSelChangeListener implements ISelectionChangedListener {
492
493 @Override
494 public void selectionChanged(SelectionChangedEvent event) {
495 IStructuredSelection selection = (IStructuredSelection) event.getSelection();
496 if (selection.isEmpty())
497 return;
498 else {
499 Path newSelected = (Path) selection.getFirstElement();
500 if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath))
501 return;
502 initialPath = newSelected;
503 setInput(newSelected);
504 }
505 }
506 }
507
508
509 private void addProperty(Composite parent, String propName, String value) {
510 Label contextL = new Label(parent, SWT.NONE);
511 contextL.setText(propName + ": " + value);
512 }
513
514 private Label appendTitle(Composite parent, String value) {
515 Label titleLbl = new Label(parent, SWT.NONE);
516 titleLbl.setText(value);
517 titleLbl.setFont(EclipseUiUtils.getBoldFont(parent));
518 GridData gd = EclipseUiUtils.fillWidth();
519 gd.horizontalIndent = 5;
520 gd.verticalIndent = 5;
521 titleLbl.setLayoutData(gd);
522 return titleLbl;
523 }
524 }