View Javadoc
1   package org.argeo.documents.ui;
2   
3   import java.io.File;
4   import java.io.FileInputStream;
5   import java.io.FileOutputStream;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.io.OutputStream;
9   import java.lang.reflect.Method;
10  import java.net.URI;
11  import java.nio.file.DirectoryNotEmptyException;
12  import java.nio.file.FileVisitResult;
13  import java.nio.file.Files;
14  import java.nio.file.Path;
15  import java.nio.file.Paths;
16  import java.nio.file.SimpleFileVisitor;
17  import java.nio.file.attribute.BasicFileAttributes;
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.jcr.Node;
25  import javax.jcr.Property;
26  import javax.jcr.PropertyType;
27  import javax.jcr.Repository;
28  import javax.jcr.RepositoryException;
29  import javax.jcr.Session;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.argeo.connect.ui.widgets.SingleQuestion;
34  import org.argeo.connect.util.ConnectJcrUtils;
35  import org.argeo.documents.DocumentsException;
36  import org.argeo.documents.DocumentsService;
37  import org.argeo.eclipse.ui.EclipseUiUtils;
38  import org.argeo.eclipse.ui.specific.OpenFile;
39  import org.argeo.jcr.JcrUtils;
40  import org.eclipse.jface.dialogs.MessageDialog;
41  import org.eclipse.jface.viewers.IStructuredSelection;
42  import org.eclipse.swt.SWT;
43  import org.eclipse.swt.widgets.FileDialog;
44  import org.eclipse.swt.widgets.Shell;
45  
46  public class DocumentsUiService {
47  	private final static Log log = LogFactory.getLog(DocumentsUiService.class);
48  
49  	// Default known actions
50  	public final static String ACTION_ID_CREATE_FOLDER = "createFolder";
51  	public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder";
52  	public final static String ACTION_ID_SHARE_FOLDER = "shareFolder";
53  	public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder";
54  	public final static String ACTION_ID_RENAME = "rename";
55  	public final static String ACTION_ID_DELETE = "delete";
56  	public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles";
57  	//public final static String ACTION_ID_OPEN = "open";
58  	public final static String ACTION_ID_DELETE_BOOKMARK = "deleteBookmark";
59  	public final static String ACTION_ID_RENAME_BOOKMARK = "renameBookmark";
60  
61  	public String getLabel(String actionId) {
62  		switch (actionId) {
63  		case ACTION_ID_CREATE_FOLDER:
64  			return "Create Folder";
65  		case ACTION_ID_BOOKMARK_FOLDER:
66  			return "Bookmark Folder";
67  		case ACTION_ID_SHARE_FOLDER:
68  			return "Share Folder";
69  		case ACTION_ID_DOWNLOAD_FOLDER:
70  			return "Download as zip archive";
71  		case ACTION_ID_RENAME:
72  			return "Rename";
73  		case ACTION_ID_DELETE:
74  			return "Delete";
75  		case ACTION_ID_UPLOAD_FILE:
76  			return "Upload Files";
77  //		case ACTION_ID_OPEN:
78  //			return "Open";
79  		case ACTION_ID_DELETE_BOOKMARK:
80  			return "Delete bookmark";
81  		case ACTION_ID_RENAME_BOOKMARK:
82  			return "Rename bookmark";
83  		default:
84  			throw new IllegalArgumentException("Unknown action ID " + actionId);
85  		}
86  	}
87  
88  	public void openFile(Path toOpenPath) {
89  		try {
90  			String name = toOpenPath.getFileName().toString();
91  			File tmpFile = File.createTempFile("tmp", name);
92  			tmpFile.deleteOnExit();
93  			try (OutputStream os = new FileOutputStream(tmpFile)) {
94  				Files.copy(toOpenPath, os);
95  			} catch (IOException e) {
96  				throw new DocumentsException("Cannot open copy " + name + " to tmpFile.", e);
97  			}
98  			String uri = Paths.get(tmpFile.getAbsolutePath()).toUri().toString();
99  			Map<String, String> params = new HashMap<String, String>();
100 			params.put(OpenFile.PARAM_FILE_NAME, name);
101 			params.put(OpenFile.PARAM_FILE_URI, uri);
102 			// FIXME open file without a command
103 			// CommandUtils.callCommand(OpenFile.ID, params);
104 		} catch (IOException e1) {
105 			throw new DocumentsException("Cannot create tmp copy of " + toOpenPath, e1);
106 		}
107 	}
108 
109 	public boolean deleteItems(Shell shell, IStructuredSelection selection) {
110 		if (selection.isEmpty())
111 			return false;
112 
113 		StringBuilder builder = new StringBuilder();
114 		@SuppressWarnings("unchecked")
115 		Iterator<Object> iterator = selection.iterator();
116 		List<Path> paths = new ArrayList<>();
117 
118 		while (iterator.hasNext()) {
119 			Path path = (Path) iterator.next();
120 			builder.append(path.getFileName() + ", ");
121 			paths.add(path);
122 		}
123 		String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2)
124 				+ ". Are you sure?";
125 		if (MessageDialog.openConfirm(shell, "Confirm deletion", msg)) {
126 			for (Path path : paths) {
127 				try {
128 					// recursively delete directory and its content
129 					Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
130 						@Override
131 						public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
132 							Files.delete(file);
133 							return FileVisitResult.CONTINUE;
134 						}
135 
136 						@Override
137 						public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
138 							Files.delete(dir);
139 							return FileVisitResult.CONTINUE;
140 						}
141 					});
142 				} catch (DirectoryNotEmptyException e) {
143 					String errMsg = path.getFileName() + " cannot be deleted: directory is not empty.";
144 					MessageDialog.openError(shell, "Deletion forbidden", errMsg);
145 					throw new DocumentsException("Cannot delete path " + path, e);
146 				} catch (IOException e) {
147 					String errMsg = e.toString();
148 					MessageDialog.openError(shell, "Deletion forbidden", errMsg);
149 					throw new DocumentsException("Cannot delete path " + path, e);
150 				}
151 			}
152 			return true;
153 		}
154 		return false;
155 	}
156 
157 	public boolean renameItem(Shell shell, Path parentFolderPath, Path toRenamePath) {
158 		String msg = "Enter a new name:";
159 		String name = SingleQuestion.ask("Rename item", msg, toRenamePath.getFileName().toString());
160 		// TODO enhance check of name validity
161 		if (EclipseUiUtils.notEmpty(name)) {
162 			try {
163 				Path child = parentFolderPath.resolve(name);
164 				if (Files.exists(child)) {
165 					String errMsg = "An object named " + name + " already exists at " + parentFolderPath.toString()
166 							+ ", please provide another name";
167 					MessageDialog.openError(shell, "Invallid new name: duplicated entry", errMsg);
168 					throw new DocumentsException(errMsg);
169 				} else {
170 					Files.move(toRenamePath, child);
171 					return true;
172 				}
173 			} catch (IOException e) {
174 				throw new DocumentsException("Cannot create folder " + name + " at " + parentFolderPath.toString(), e);
175 			}
176 		}
177 		return false;
178 	}
179 
180 	public boolean createFolder(Shell shell, Path currFolderPath) {
181 		String msg = "Enter a name:";
182 		String name = SingleQuestion.ask("Create folder", msg);
183 		// TODO enhance check of name validity
184 		if (EclipseUiUtils.notEmpty(name)) {
185 			name = name.trim();
186 			try {
187 				Path child = currFolderPath.resolve(name);
188 				if (Files.exists(child)) {
189 					String errMsg = "A folder named " + name + " already exists at " + currFolderPath.toString()
190 							+ ", cannot create";
191 					MessageDialog.openError(shell, "Creation forbidden: dupplicated name", errMsg);
192 					throw new DocumentsException(errMsg);
193 				} else {
194 					Files.createDirectories(child);
195 					return true;
196 				}
197 			} catch (IOException e) {
198 				throw new DocumentsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e);
199 			}
200 		}
201 		return false;
202 	}
203 
204 	public void bookmarkFolder(Path toBookmarkPath, Repository repository, DocumentsService documentsService) {
205 		String msg = "Provide a name:";
206 		String name = SingleQuestion.ask("Create bookmark", msg, toBookmarkPath.getFileName().toString());
207 		if (EclipseUiUtils.notEmpty(name))
208 			documentsService.createFolderBookmark(toBookmarkPath, name, repository);
209 	}
210 
211 	public boolean uploadFiles(Shell shell, Path currFolderPath) {
212 //		shell = Display.getCurrent().getActiveShell();// ignore argument
213 		try {
214 			FileDialog dialog = new FileDialog(shell, SWT.MULTI);
215 			dialog.setText("Choose one or more files to upload");
216 
217 			if (EclipseUiUtils.notEmpty(dialog.open())) {
218 				String[] names = dialog.getFileNames();
219 				// Workaround small differences between RAP and RCP
220 				// 1. returned names are absolute path on RAP and
221 				// relative in RCP
222 				// 2. in RCP we must use getFilterPath that does not
223 				// exists on RAP
224 				Method filterMethod = null;
225 				Path parPath = null;
226 				try {
227 					filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath");
228 					String filterPath = (String) filterMethod.invoke(dialog);
229 					parPath = Paths.get(filterPath);
230 				} catch (NoSuchMethodException nsme) { // RAP
231 				}
232 				if (names.length == 0)
233 					return false;
234 				else {
235 					loop: for (String name : names) {
236 						Path tmpPath = Paths.get(name);
237 						if (parPath != null)
238 							tmpPath = parPath.resolve(tmpPath);
239 						if (Files.exists(tmpPath)) {
240 							URI uri = tmpPath.toUri();
241 							String uriStr = uri.toString();
242 
243 							if (Files.isDirectory(tmpPath)) {
244 								MessageDialog.openError(shell, "Unimplemented directory import",
245 										"Upload of directories in the system is not yet implemented");
246 								continue loop;
247 							}
248 							Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString());
249 							try (InputStream in = new FileInputStream(tmpPath.toFile())) {
250 								Files.copy(in, targetPath);
251 								Files.delete(tmpPath);
252 							}
253 							if (log.isDebugEnabled())
254 								log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString());
255 						} else {
256 							String msg = "Cannot copy tmp file from " + tmpPath.toString();
257 							if (parPath != null)
258 								msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS";
259 							MessageDialog.openError(shell, "Missing file", msg);
260 							continue loop;
261 						}
262 					}
263 					return true;
264 				}
265 			}
266 		} catch (Exception e) {
267 			e.printStackTrace();
268 			MessageDialog.openError(shell, "Upload has failed", "Cannot import files to " + currFolderPath);
269 		}
270 		return false;
271 	}
272 
273 	public boolean deleteBookmark(Shell shell, IStructuredSelection selection, Node bookmarkParent) {
274 		if (selection.isEmpty())
275 			return false;
276 
277 		StringBuilder builder = new StringBuilder();
278 		@SuppressWarnings("unchecked")
279 		Iterator<Object> iterator = selection.iterator();
280 		List<Node> nodes = new ArrayList<>();
281 
282 		while (iterator.hasNext()) {
283 			Node node = (Node) iterator.next();
284 			builder.append(ConnectJcrUtils.get(node, Property.JCR_TITLE) + ", ");
285 			nodes.add(node);
286 		}
287 		String msg = "You are about to delete following bookmark: " + builder.substring(0, builder.length() - 2)
288 				+ ". Are you sure?";
289 		if (MessageDialog.openConfirm(shell, "Confirm deletion", msg)) {
290 			Session session = ConnectJcrUtils.getSession(bookmarkParent);
291 			try {
292 				if (session.hasPendingChanges())
293 					throw new DocumentsException("Cannot remove bookmarks, session is not clean");
294 				for (Node path : nodes)
295 					path.remove();
296 				bookmarkParent.getSession().save();
297 				return true;
298 			} catch (RepositoryException e) {
299 				JcrUtils.discardQuietly(session);
300 				throw new DocumentsException("Cannot delete bookmarks " + builder.toString(), e);
301 			}
302 		}
303 		return false;
304 	}
305 
306 	public boolean renameBookmark(IStructuredSelection selection) {
307 		if (selection.isEmpty() || selection.size() > 1)
308 			return false;
309 		Node toRename = (Node) selection.getFirstElement();
310 		String msg = "Please provide a new name.";
311 		String name = SingleQuestion.ask("Rename bookmark", msg, ConnectJcrUtils.get(toRename, Property.JCR_TITLE));
312 		if (EclipseUiUtils.notEmpty(name)
313 				&& ConnectJcrUtils.setJcrProperty(toRename, Property.JCR_TITLE, PropertyType.STRING, name)) {
314 			ConnectJcrUtils.saveIfNecessary(toRename);
315 			return true;
316 		}
317 		return false;
318 	}
319 }