1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.exoplatform.services.cms.documents.impl;
18
19 import org.exoplatform.container.ExoContainer;
20 import org.exoplatform.container.ExoContainerContext;
21 import org.exoplatform.container.xml.InitParams;
22 import org.exoplatform.services.cache.CacheService;
23 import org.exoplatform.services.cache.ExoCache;
24 import org.exoplatform.services.cms.documents.TrashService;
25 import org.exoplatform.services.cms.folksonomy.NewFolksonomyService;
26 import org.exoplatform.services.cms.impl.Utils;
27 import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
28 import org.exoplatform.services.cms.link.LinkManager;
29 import org.exoplatform.services.cms.taxonomy.TaxonomyService;
30 import org.exoplatform.services.jcr.RepositoryService;
31 import org.exoplatform.services.jcr.access.PermissionType;
32 import org.exoplatform.services.jcr.core.ManageableRepository;
33 import org.exoplatform.services.jcr.ext.common.SessionProvider;
34 import org.exoplatform.services.jcr.impl.core.ItemImpl;
35 import org.exoplatform.services.jcr.impl.core.SessionImpl;
36 import org.exoplatform.services.jcr.impl.core.query.QueryImpl;
37 import org.exoplatform.services.listener.ListenerService;
38 import org.exoplatform.services.log.ExoLogger;
39 import org.exoplatform.services.log.Log;
40 import org.exoplatform.services.seo.SEOService;
41 import org.exoplatform.services.wcm.core.NodetypeConstant;
42 import org.exoplatform.services.wcm.utils.WCMCoreUtils;
43 import org.gatein.pc.api.PortletInvoker;
44 import org.gatein.pc.api.info.PortletInfo;
45 import org.gatein.pc.api.info.PreferencesInfo;
46
47 import javax.jcr.Node;
48 import javax.jcr.NodeIterator;
49 import javax.jcr.RepositoryException;
50 import javax.jcr.Session;
51 import javax.jcr.query.Query;
52 import javax.jcr.query.QueryManager;
53 import javax.jcr.query.QueryResult;
54
55 import java.util.ArrayList;
56 import java.util.List;
57 import java.util.Set;
58
59
60
61
62
63 public class TrashServiceImpl implements TrashService {
64
65 private static final String FILE_EXPLORER_PORTLET = "FileExplorerPortlet";
66 private final static String CACHE_NAME = "ecms.seo";
67 final static public String EXO_TOTAL = "exo:total";
68 final static public String MIX_REFERENCEABLE = "mix:referenceable";
69 final static public String TAXONOMY_LINK = "exo:taxonomyLink";
70 final static public String UUID = "exo:uuid";
71 final static public String SYMLINK = "exo:symlink";
72 final static public String EXO_WORKSPACE = "exo:workspace";
73 final static public String EXO_TARGETWS = "exo:targetWorkspace";
74 final static public String EXO_TARGETPATH = "exo:targetPath";
75
76 private RepositoryService repositoryService;
77 private LinkManager linkManager;
78 private TaxonomyService taxonomyService_;
79 private String trashWorkspace_;
80 private String trashHome_;
81 private ExoCache<String, Object> cache;
82
83
84 private static final Log LOG = ExoLogger.getLogger(TrashServiceImpl.class.getName());
85
86 public TrashServiceImpl(RepositoryService repositoryService,
87 LinkManager linkManager,
88 TaxonomyService taxonomyService,
89 InitParams initParams) throws Exception {
90 this.repositoryService = repositoryService;
91 this.linkManager = linkManager;
92 this.taxonomyService_ = taxonomyService;
93 this.trashWorkspace_ = initParams.getValueParam("trashWorkspace").getValue();
94 this.trashHome_ = initParams.getValueParam("trashHomeNodePath").getValue();
95 cache = WCMCoreUtils.getService(CacheService.class).getCacheInstance(CACHE_NAME);
96 ExoContainer manager = ExoContainerContext.getCurrentContainer();
97 PortletInvoker portletInvoker = (PortletInvoker)manager.getComponentInstance(PortletInvoker.class);
98 if (portletInvoker != null) {
99 Set<org.gatein.pc.api.Portlet> portlets = portletInvoker.getPortlets();
100 for (org.gatein.pc.api.Portlet portlet : portlets) {
101 PortletInfo info = portlet.getInfo();
102 String portletName = info.getName();
103 if (FILE_EXPLORER_PORTLET.equalsIgnoreCase(portletName)) {
104 PreferencesInfo prefs = info.getPreferences();
105 String trashWorkspace = prefs.getPreference("trashWorkspace").getDefaultValue().get(0);
106 String trashHome = prefs.getPreference("trashHomeNodePath").getDefaultValue().get(0);
107 if (trashWorkspace != null && !trashWorkspace.equals(this.trashWorkspace_)) {
108 this.trashWorkspace_ = trashWorkspace;
109 }
110
111 if (trashHome != null && !trashHome.equals(this.trashHome_)) {
112 this.trashHome_ = trashHome;
113 }
114 break;
115 }
116 }
117 }
118 }
119
120
121
122
123
124 public String moveToTrash(Node node, SessionProvider sessionProvider) throws Exception {
125 return moveToTrash(node, sessionProvider, 0);
126 }
127
128
129
130
131
132 @Override
133 public String moveToTrash(Node node,
134 SessionProvider sessionProvider,
135 int deep) throws Exception {
136 ((SessionImpl)node.getSession()).getActionHandler().preRemoveItem((ItemImpl)node);
137 String trashId="-1";
138 String nodeName = node.getName();
139 Session nodeSession = node.getSession();
140 nodeSession.checkPermission(node.getPath(), PermissionType.REMOVE);
141 if (deep == 0 && !node.isNodeType(SYMLINK)) {
142 try {
143 Utils.removeDeadSymlinks(node);
144 } catch (Exception e) {
145 if (LOG.isWarnEnabled()) {
146 LOG.warn(e.getMessage());
147 }
148 }
149 }
150 ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class);
151
152 if (node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE) || node.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
153 ActivityCommonService activityService = WCMCoreUtils.getService(ActivityCommonService.class);
154 if (activityService.isBroadcastNTFileEvents(node)) {
155 listenerService.broadcast(ActivityCommonService.FILE_REMOVE_ACTIVITY, null, node);
156 }
157 } else{
158 listenerService.broadcast(ActivityCommonService.FILE_REMOVE_ACTIVITY, null, node);
159 }
160 String originalPath = node.getPath();
161 String nodeWorkspaceName = nodeSession.getWorkspace().getName();
162
163 String nodeUUID = node.isNodeType(MIX_REFERENCEABLE) ? node.getUUID() : null;
164 if (node.isNodeType(SYMLINK)) nodeUUID = null;
165 String taxonomyLinkUUID = node.isNodeType(TAXONOMY_LINK) ? node.getProperty(UUID).getString() : null;
166 String taxonomyLinkWS = node.isNodeType(TAXONOMY_LINK) ? node.getProperty(EXO_WORKSPACE).getString() : null;
167 if(nodeUUID != null) {
168 SEOService seoService = WCMCoreUtils.getService(SEOService.class);
169 cache.remove(seoService.getHash(nodeUUID));
170 }
171 if (!node.isNodeType(EXO_RESTORE_LOCATION)) {
172 String restorePath = fixRestorePath(node.getPath());
173 ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
174 Session trashSession = WCMCoreUtils.getSystemSessionProvider().getSession(this.trashWorkspace_, manageableRepository);
175 String actualTrashPath = this.trashHome_ + (this.trashHome_.endsWith("/") ? "" : "/")
176 + fixRestorePath(nodeName);
177 if (trashSession.getWorkspace().getName().equals(
178 nodeSession.getWorkspace().getName())) {
179 trashSession.getWorkspace().move(node.getPath(),
180 actualTrashPath);
181 } else {
182
183 trashSession.getWorkspace().clone(nodeWorkspaceName,
184 node.getPath(), actualTrashPath, true);
185 if (node.isNodeType(MIX_REFERENCEABLE)) {
186 Node clonedNode = trashSession.getNodeByUUID(node.getUUID());
187
188
189 NewFolksonomyService newFolksonomyService = WCMCoreUtils.getService(NewFolksonomyService.class);
190
191 String tagWorkspace = manageableRepository.getConfiguration().getDefaultWorkspaceName();
192 List<Node> tags = newFolksonomyService.getLinkedTagsOfDocument(node, tagWorkspace);
193 for (Node tag : tags) {
194 newFolksonomyService.removeTagOfDocument(tag.getPath(), node, tagWorkspace);
195 linkManager.createLink(tag, clonedNode);
196 long total = tag.hasProperty(EXO_TOTAL) ?
197 tag.getProperty(EXO_TOTAL).getLong() : 0;
198 tag.setProperty(EXO_TOTAL, total - 1);
199 tag.getSession().save();
200 }
201 }
202 node.remove();
203 }
204
205 trashId = addRestorePathInfo(nodeName, restorePath, nodeWorkspaceName);
206
207 trashSession.save();
208
209
210 if (deep == 0 && taxonomyLinkUUID != null && taxonomyLinkWS != null) {
211 Session targetNodeSession = sessionProvider.getSession(taxonomyLinkWS, manageableRepository);
212 Node targetNode = null;
213 try {
214 targetNode = targetNodeSession.getNodeByUUID(taxonomyLinkUUID);
215 } catch (Exception e) {
216 if (LOG.isWarnEnabled()) {
217 LOG.warn(e.getMessage());
218 }
219 }
220 if (targetNode != null && isInTaxonomyTree(originalPath, targetNode)) {
221 List<Node> symlinks = linkManager.getAllLinks(targetNode, SYMLINK, sessionProvider);
222 boolean found = false;
223 for (Node symlink : symlinks)
224 if (!symlink.isNodeType(EXO_RESTORE_LOCATION)) {
225 found = true;
226 break;
227 }
228 if (!found) {
229 this.moveToTrash(targetNode, sessionProvider);
230 }
231 }
232 }
233
234 trashSession.save();
235 }
236 return trashId;
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250 private String addRestorePathInfo(String nodeName, String restorePath, String nodeWs) throws Exception {
251 String restoreId = java.util.UUID.randomUUID().toString();
252 NodeIterator nodes = this.getTrashHomeNode().getNodes(nodeName);
253 Node node = null;
254 while (nodes.hasNext()) {
255 Node currentNode = nodes.nextNode();
256 if (node == null) {
257 node = currentNode;
258 } else {
259 if (node.getIndex() < currentNode.getIndex()) {
260 node = currentNode;
261 }
262 }
263 }
264 if (node != null) {
265 node.addMixin(EXO_RESTORE_LOCATION);
266 node.setProperty(RESTORE_PATH, restorePath);
267 node.setProperty(RESTORE_WORKSPACE, nodeWs);
268 node.setProperty(TRASH_ID, restoreId);
269 node.save();
270 }
271 return restoreId;
272 }
273
274
275
276
277
278
279
280 private boolean isInTaxonomyTree(String path, Node targetNode) {
281 try {
282 List<Node> taxonomyTrees = taxonomyService_.getAllTaxonomyTrees(true);
283 for (Node tree : taxonomyTrees)
284 if (path.contains(tree.getPath())) {
285 Node taxonomyActionNode = tree.getNode("exo:actions/taxonomyAction");
286 String targetWorkspace = taxonomyActionNode.getProperty(EXO_TARGETWS).getString();
287 String targetPath = taxonomyActionNode.getProperty(EXO_TARGETPATH).getString();
288 if (targetNode.getSession().getWorkspace().getName().equals(targetWorkspace)
289 && targetNode.getPath().contains(targetPath))
290 return true;
291 break;
292 }
293 return false;
294 } catch (Exception e) {
295 return false;
296 }
297 }
298
299
300
301
302
303 public void restoreFromTrash(String trashNodePath,
304 SessionProvider sessionProvider) throws Exception {
305 restoreFromTrash(trashNodePath, sessionProvider, 0);
306 }
307
308 private void restoreFromTrash(String trashNodePath,
309 SessionProvider sessionProvider, int deep) throws Exception {
310
311 Node trashHomeNode = this.getTrashHomeNode();
312 Session trashNodeSession = trashHomeNode.getSession();
313 Node trashNode = (Node)trashNodeSession.getItem(trashNodePath);
314 String trashWorkspace = trashNodeSession.getWorkspace().getName();
315 String restoreWorkspace = trashNode.getProperty(RESTORE_WORKSPACE).getString();
316 String restorePath = trashNode.getProperty(RESTORE_PATH).getString();
317 String nodeUUID = trashNode.isNodeType(MIX_REFERENCEABLE) ? trashNode.getUUID() : null;
318 if (trashNode.isNodeType(SYMLINK)) nodeUUID = null;
319 String taxonomyLinkUUID = trashNode.isNodeType(TAXONOMY_LINK) ? trashNode.getProperty(UUID).getString() : null;
320 String taxonomyLinkWS = trashNode.isNodeType(TAXONOMY_LINK) ? trashNode.getProperty(EXO_WORKSPACE).getString() : null;
321
322 ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
323 Session restoreSession = sessionProvider.getSession(restoreWorkspace, manageableRepository);
324
325 if (restoreWorkspace.equals(trashWorkspace)) {
326 trashNodeSession.getWorkspace().move(trashNodePath, restorePath);
327 } else {
328
329 restoreSession.getWorkspace().clone(
330 trashWorkspace, trashNodePath, restorePath, true);
331 if (trashNode.isNodeType(MIX_REFERENCEABLE)) {
332 Node restoredNode = restoreSession.getNodeByUUID(trashNode.getUUID());
333
334
335 NewFolksonomyService newFolksonomyService = WCMCoreUtils.getService(NewFolksonomyService.class);
336
337 String tagWorkspace = manageableRepository.getConfiguration().getDefaultWorkspaceName();
338 List<Node> tags = newFolksonomyService.getLinkedTagsOfDocument(trashNode, tagWorkspace);
339 for (Node tag : tags) {
340 newFolksonomyService.removeTagOfDocument(tag.getPath(), trashNode, tagWorkspace);
341 linkManager.createLink(tag, restoredNode);
342 long total = tag.hasProperty(EXO_TOTAL) ?
343 tag.getProperty(EXO_TOTAL).getLong() : 0;
344 tag.setProperty(EXO_TOTAL, total + 1);
345 tag.getSession().save();
346 }
347 }
348
349 trashNodeSession.getItem(trashNodePath).remove();
350 }
351
352 removeMixinEXO_RESTORE_LOCATION(restoreSession, restorePath);
353
354 trashNodeSession.save();
355 restoreSession.save();
356
357
358 if (deep == 0 && nodeUUID != null) {
359 while (true) {
360 boolean found = false;
361 NodeIterator iter = trashHomeNode.getNodes();
362 while (iter.hasNext()) {
363 Node trashChild = iter.nextNode();
364 if (trashChild.isNodeType(TAXONOMY_LINK) && trashChild.hasProperty(UUID)
365 && trashChild.hasProperty(EXO_WORKSPACE)
366 && nodeUUID.equals(trashChild.getProperty(UUID).getString())
367 && restoreWorkspace.equals(trashChild.getProperty(EXO_WORKSPACE))) {
368 try {
369 restoreFromTrash(trashChild.getPath(), sessionProvider, deep + 1);
370 found = true;
371 break;
372 } catch (Exception e) {
373 if (LOG.isWarnEnabled()) {
374 LOG.warn(e.getMessage());
375 }
376 }
377 }
378 }
379 if (!found) break;
380 }
381 }
382
383 trashNodeSession.save();
384 restoreSession.save();
385
386 if (deep == 0 && taxonomyLinkUUID != null && taxonomyLinkWS != null) {
387 while (true) {
388 boolean found = false;
389 NodeIterator iter = trashHomeNode.getNodes();
390 while (iter.hasNext()) {
391 Node trashChild = iter.nextNode();
392 if (trashChild.isNodeType(MIX_REFERENCEABLE)
393 && taxonomyLinkUUID.equals(trashChild.getUUID())
394 && taxonomyLinkWS.equals(trashChild.getProperty(RESTORE_WORKSPACE).getString())) {
395 try {
396 restoreFromTrash(trashChild.getPath(),
397 sessionProvider,
398 deep + 1);
399 found = true;
400 break;
401 } catch (Exception e) {
402 if (LOG.isWarnEnabled()) {
403 LOG.warn(e.getMessage());
404 }
405 }
406 }
407 }
408 if (!found) break;
409 }
410 }
411
412 trashNodeSession.save();
413 restoreSession.save();
414 }
415
416
417
418
419
420 public List<Node> getAllNodeInTrash(SessionProvider sessionProvider) throws Exception {
421
422 StringBuilder query = new StringBuilder("SELECT * FROM nt:base WHERE exo:restorePath IS NOT NULL");
423
424 return selectNodesByQuery(sessionProvider, query.toString(), Query.SQL);
425 }
426
427
428
429
430 public List<Node> getAllNodeInTrashByUser(SessionProvider sessionProvider,
431 String userName) throws Exception {
432 StringBuilder query = new StringBuilder(
433 "SELECT * FROM nt:base WHERE exo:restorePath IS NOT NULL AND exo:lastModifier='").append(userName).append("'");
434 return selectNodesByQuery(sessionProvider, query.toString(), Query.SQL);
435 }
436
437
438 public void removeRelations(Node node, SessionProvider sessionProvider) throws Exception {
439 ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
440 String[] workspaces = manageableRepository.getWorkspaceNames();
441
442 String queryString = "SELECT * FROM exo:relationable WHERE exo:relation IS NOT NULL";
443 boolean error = false;
444
445 for (String ws : workspaces) {
446 Session session = sessionProvider.getSession(ws, manageableRepository);
447 QueryManager queryManager = session.getWorkspace().getQueryManager();
448 Query query = queryManager.createQuery(queryString, Query.SQL);
449 QueryResult queryResult = query.execute();
450
451 NodeIterator iter = queryResult.getNodes();
452 while (iter.hasNext()) {
453 try {
454 iter.nextNode().removeMixin("exo:relationable");
455 session.save();
456 } catch (Exception e) {
457 error = true;
458 }
459 }
460 }
461 if (error) throw new Exception("Can't remove exo:relationable of all related nodes");
462 }
463
464
465
466
467 public boolean isInTrash(Node node) throws RepositoryException {
468 return node.getPath().startsWith(this.trashHome_) && !node.getPath().equals(this.trashHome_);
469 }
470
471
472
473
474 public Node getTrashHomeNode() {
475 try {
476 Session session = WCMCoreUtils.getSystemSessionProvider()
477 .getSession(trashWorkspace_,
478 repositoryService.getCurrentRepository());
479 return (Node) session.getItem(trashHome_);
480 } catch (Exception e) {
481 return null;
482 }
483
484 }
485
486 public Node getNodeByTrashId(String trashId) throws RepositoryException{
487 QueryResult queryResult;
488 NodeIterator iter;
489 Session session = WCMCoreUtils.getSystemSessionProvider()
490 .getSession(trashWorkspace_,
491 repositoryService.getCurrentRepository());
492 QueryManager queryManager = session.getWorkspace().getQueryManager();
493 StringBuilder sb = new StringBuilder();
494 sb.append("SELECT * from exo:restoreLocation WHERE exo:trashId = '").append(trashId).append("'");
495 QueryImpl query = (QueryImpl) queryManager.createQuery(sb.toString(), Query.SQL);
496 query.setLimit(1);
497 queryResult = query.execute();
498 iter = queryResult.getNodes();
499 if(iter.hasNext()) return iter.nextNode();
500 else return null;
501 }
502
503
504 private List<Node> selectNodesByQuery(SessionProvider sessionProvider,
505 String queryString,
506 String language) throws Exception {
507 List<Node> ret = new ArrayList<Node>();
508 ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
509 Session session = sessionProvider.getSession(this.trashWorkspace_, manageableRepository);
510 QueryManager queryManager = session.getWorkspace().getQueryManager();
511 Query query = queryManager.createQuery(queryString, language);
512 QueryResult queryResult = query.execute();
513
514 NodeIterator iter = queryResult.getNodes();
515 while (iter.hasNext()) {
516 ret.add(iter.nextNode());
517 }
518
519 return ret;
520 }
521
522 private String fixRestorePath(String path) {
523 int leftBracket = path.lastIndexOf('[');
524 int rightBracket = path.lastIndexOf(']');
525 if (leftBracket == -1 || rightBracket == -1 ||
526 (leftBracket >= rightBracket)) return path;
527
528 try {
529 Integer.parseInt(path.substring(leftBracket+1, rightBracket));
530 } catch (Exception ex) {
531 return path;
532 }
533 return path.substring(0, leftBracket);
534 }
535
536 private void removeMixinEXO_RESTORE_LOCATION(Session session, String restorePath) throws Exception {
537 Node sameNameNode = ((Node) session.getItem(restorePath));
538 Node parent = sameNameNode.getParent();
539 String name = sameNameNode.getName();
540 NodeIterator nodeIter = parent.getNodes(name);
541 while (nodeIter.hasNext()) {
542 Node node = nodeIter.nextNode();
543 if (node.isNodeType(EXO_RESTORE_LOCATION))
544 node.removeMixin(EXO_RESTORE_LOCATION);
545 }
546 }
547
548 }