1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package com.xpn.xwiki.web

File UndeleteAction.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
41% of files have more coverage

Code metrics

24
71
8
1
252
155
26
0.37
8.88
8
3.25

Classes

Class Line # Actions
UndeleteAction 50 71 0% 26 13
0.873786487.4%
 

Contributing tests

This file is covered by 7 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.web;
21   
22    import java.util.Arrays;
23    import java.util.List;
24    import java.util.Locale;
25   
26    import org.apache.commons.lang3.StringUtils;
27    import org.slf4j.Logger;
28    import org.slf4j.LoggerFactory;
29    import org.xwiki.job.Job;
30    import org.xwiki.job.JobException;
31    import org.xwiki.job.JobExecutor;
32    import org.xwiki.refactoring.job.RefactoringJobs;
33    import org.xwiki.refactoring.job.RestoreRequest;
34    import org.xwiki.refactoring.script.RefactoringScriptService;
35    import org.xwiki.script.service.ScriptService;
36   
37    import com.xpn.xwiki.XWiki;
38    import com.xpn.xwiki.XWikiContext;
39    import com.xpn.xwiki.XWikiException;
40    import com.xpn.xwiki.api.DeletedDocument;
41    import com.xpn.xwiki.doc.XWikiDeletedDocument;
42    import com.xpn.xwiki.doc.XWikiDocument;
43   
44    /**
45    * Action for restoring documents from the recycle bin.
46    *
47    * @version $Id: 0dcaed69e337a89189be7595f7dea09210a0af45 $
48    * @since 1.2M1
49    */
 
50    public class UndeleteAction extends XWikiAction
51    {
52    private static final String ID_PARAMETER = "id";
53   
54    private static final String SHOW_BATCH_PARAMETER = "showBatch";
55   
56    private static final String INCLUDE_BATCH_PARAMETER = "includeBatch";
57   
58    private static final String CONFIRM_PARAMETER = "confirm";
59   
60    private static final String ASYNC_PARAM = "async";
61   
62    private static final String TRUE = "true";
63   
64    private static final String VIEW_ACTION = "view";
65   
66    private static final Logger LOGGER = LoggerFactory.getLogger(UndeleteAction.class);
67   
 
68  10 toggle @Override
69    public boolean action(XWikiContext context) throws XWikiException
70    {
71  10 XWikiRequest request = context.getRequest();
72  10 XWikiResponse response = context.getResponse();
73  10 XWikiDocument doc = context.getDoc();
74   
75    // If the provided DeletedDocument ID is invalid, for any reason, redirect to view mode to see the document does
76    // not exist screen.
77  10 XWikiDeletedDocument deletedDocument = getDeletedDocument(context);
78  10 if (deletedDocument == null) {
79  1 sendRedirect(response, doc.getURL(VIEW_ACTION, context));
80  1 return false;
81    }
82   
83    // Note: Check the ID validity before checking the showBatch parameter so that we validate the ID before
84    // displaying any restore batch UI.
85   
86    // If showBatch=true and confirm=true then restore the page w/o the batch. If not, the render action will go to
87    // the "restore" UI so that the user can confirm. That "restore" UI will then call the action again with
88    // confirm=true.
89  9 if (TRUE.equals(request.getParameter(SHOW_BATCH_PARAMETER))
90    && !TRUE.equals(request.getParameter(CONFIRM_PARAMETER))) {
91  1 return true;
92    }
93   
94    // CSRF prevention
95  8 if (!csrfTokenCheck(context)) {
96  1 return false;
97    }
98   
99    // If the current user is not allowed to restore, render the "accessdenied" template.
100  7 DeletedDocument deletedDocumentAPI = new DeletedDocument(deletedDocument, context);
101  7 if (!deletedDocumentAPI.canUndelete()) {
102  2 return true;
103    }
104   
105  5 boolean redirected = false;
106   
107  5 if (deletedDocument != null) {
108  5 redirected = restoreDocument(deletedDocument, context);
109    }
110   
111    // Redirect to the undeleted document. Make sure to redirect to the proper translation.
112  5 if (!redirected) {
113  5 String queryString = getRedirectQueryString(context, deletedDocument.getLocale());
114  5 sendRedirect(response, doc.getURL(VIEW_ACTION, queryString, context));
115  5 redirected = true;
116    }
117   
118  5 return !redirected;
119    }
120   
 
121  13 toggle private XWikiDeletedDocument getDeletedDocument(XWikiContext context) throws XWikiException
122    {
123  13 XWikiDeletedDocument result = null;
124   
125  13 XWikiRequest request = context.getRequest();
126  13 XWiki xwiki = context.getWiki();
127   
128  13 String sindex = request.getParameter(ID_PARAMETER);
129  13 try {
130  13 long index = Long.parseLong(sindex);
131   
132  13 result = xwiki.getDeletedDocument(index, context);
133    } catch (Exception e) {
134  0 LOGGER.error("Failed to get deleted document with ID [{}]", sindex, e);
135    }
136   
137  13 return result;
138    }
139   
 
140  5 toggle private boolean restoreDocument(XWikiDeletedDocument deletedDocument, XWikiContext context) throws XWikiException
141    {
142  5 Job restoreJob = startRestoreJob(deletedDocument, context);
143   
144    // If the user asked for an asynchronous action...
145  5 if (isAsync(context.getRequest())) {
146  0 List<String> jobId = restoreJob.getRequest().getId();
147   
148    // Note: We use spaceRedirect=false to fix the link to the document when view mode would normally try to
149    // modify the context document to a non-terminal one but the restored document is actually terminal.
150  0 String queryString = "xpage=restore&jobId=" + serializeJobId(jobId) + "&spaceRedirect=false";
151   
152    // We redirect to the view action and accept the edge case when the restored document's rights might prevent
153    // the restoring user to view the result. In that case, an admin must be contacted to fix the rights.
154  0 sendRedirect(context.getResponse(), Utils.getRedirect(VIEW_ACTION, queryString, context));
155   
156    // A redirect has been performed.
157  0 return true;
158    }
159   
160    // Otherwise...
161  5 try {
162  5 restoreJob.join();
163    } catch (InterruptedException e) {
164  0 throw new XWikiException(String.format("Failed to restore [%s] from batch [%s]",
165    deletedDocument.getFullName(), deletedDocument.getBatchId()), e);
166    }
167   
168    // No redirect has been performed.
169  5 return false;
170    }
171   
 
172  0 toggle private String serializeJobId(List<String> jobId)
173    {
174  0 return StringUtils.join(jobId, "/");
175    }
176   
 
177  5 toggle private Job startRestoreJob(XWikiDeletedDocument deletedDocument, XWikiContext context) throws XWikiException
178    {
179  5 XWikiRequest request = context.getRequest();
180   
181  5 RefactoringScriptService refactoring =
182    (RefactoringScriptService) Utils.getComponent(ScriptService.class, "refactoring");
183   
184  5 RestoreRequest restoreRequest = null;
185  5 if (TRUE.equals(request.getParameter(INCLUDE_BATCH_PARAMETER))) {
186    // Restore the entire batch, including the current document.
187  1 String batchId = deletedDocument.getBatchId();
188  1 restoreRequest = refactoring.getRequestFactory().createRestoreRequest(batchId);
189    } else {
190    // Restore just the current document.
191  4 restoreRequest = refactoring.getRequestFactory()
192    .createRestoreRequest(Arrays.asList(deletedDocument.getId()));
193    }
194  5 restoreRequest.setInteractive(isAsync(request));
195  5 restoreRequest.setCheckAuthorRights(false);
196   
197  5 try {
198  5 JobExecutor jobExecutor = Utils.getComponent(JobExecutor.class);
199  5 return jobExecutor.execute(RefactoringJobs.RESTORE, restoreRequest);
200    } catch (JobException e) {
201  0 throw new XWikiException(
202    String.format("Failed to schedule the restore job for deleted document [%s], id [%s] of batch [%s]",
203    deletedDocument.getFullName(), deletedDocument.getId(), deletedDocument.getBatchId()),
204    e);
205    }
206    }
207   
 
208  10 toggle private boolean isAsync(XWikiRequest request)
209    {
210  10 return TRUE.equals(request.get(ASYNC_PARAM));
211    }
212   
 
213  5 toggle private String getRedirectQueryString(XWikiContext context, Locale deletedDocumentLocale)
214    {
215  5 String result = null;
216   
217  5 XWiki xwiki = context.getWiki();
218   
219  5 if (deletedDocumentLocale != null && xwiki.isMultiLingual(context)) {
220  1 result = String.format("language=%s", deletedDocumentLocale);
221    }
222   
223  5 return result;
224    }
225   
 
226  3 toggle @Override
227    public String render(XWikiContext context) throws XWikiException
228    {
229  3 String result = null;
230   
231  3 XWikiRequest request = context.getRequest();
232   
233    // If showBatch=true and user confirmation is required, display the "restore" UI.
234  3 if (TRUE.equals(request.getParameter(SHOW_BATCH_PARAMETER))
235    && !TRUE.equals(request.getParameter(CONFIRM_PARAMETER))) {
236  1 result = "restore";
237    }
238   
239    // If the current user is not allowed to restore, display the "accessdenied" template.
240  3 XWikiDeletedDocument deletedDocument = getDeletedDocument(context);
241  3 if (deletedDocument != null) {
242    // Note: Checking for null because when the document is actually restored, it may no longer be in the
243    // recycle bin by the time render() gets called.
244  3 DeletedDocument deletedDocumentAPI = new DeletedDocument(deletedDocument, context);
245  3 if (!deletedDocumentAPI.canUndelete()) {
246  2 return "accessdenied";
247    }
248    }
249   
250  1 return result;
251    }
252    }