1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.sheet.internal

File SheetDocumentDisplayer.java

 

Coverage histogram

../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

14
35
6
1
241
110
15
0.43
5.83
6
2.5

Classes

Class Line # Actions
SheetDocumentDisplayer 55 35 0% 15 3
0.9454545494.5%
 

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 org.xwiki.sheet.internal;
21   
22    import java.util.List;
23    import java.util.Map;
24   
25    import javax.inject.Inject;
26    import javax.inject.Named;
27    import javax.inject.Singleton;
28   
29    import org.apache.commons.lang3.StringUtils;
30    import org.slf4j.Logger;
31    import org.xwiki.bridge.DocumentAccessBridge;
32    import org.xwiki.bridge.DocumentModelBridge;
33    import org.xwiki.component.annotation.Component;
34    import org.xwiki.display.internal.DocumentDisplayer;
35    import org.xwiki.display.internal.DocumentDisplayerParameters;
36    import org.xwiki.model.ModelContext;
37    import org.xwiki.model.reference.DocumentReference;
38    import org.xwiki.model.reference.EntityReference;
39    import org.xwiki.model.reference.EntityReferenceSerializer;
40    import org.xwiki.rendering.block.XDOM;
41    import org.xwiki.sheet.SheetManager;
42   
43    /**
44    * Displays documents by applying their corresponding sheets. Sheets are rendered in the context of the displayed
45    * document. This displayer ensures the programming rights of the sheet are preserved: if the sheet doesn't have
46    * programming rights then it is evaluated without them, otherwise, if the sheet has programming rights, it is evaluated
47    * with programming rights even if the displayed document doesn't have them.
48    *
49    * @version $Id: cb5837cd2bb0e32886a979337399be2f830bdeb5 $
50    * @since 3.2M3
51    */
52    @Component
53    @Named("sheet")
54    @Singleton
 
55    public class SheetDocumentDisplayer implements DocumentDisplayer
56    {
57    /** Logging helper object. */
58    @Inject
59    private Logger logger;
60   
61    /**
62    * The document displayer used to display the sheets.
63    */
64    @Inject
65    private DocumentDisplayer documentDisplayer;
66   
67    /**
68    * The component used to retrieve the sheet corresponding to a given document.
69    */
70    @Inject
71    private SheetManager sheetManager;
72   
73    /**
74    * The document access bridge.
75    */
76    @Inject
77    private DocumentAccessBridge documentAccessBridge;
78   
79    /**
80    * The component used to access the XWiki model.
81    */
82    @Inject
83    private ModelBridge modelBridge;
84   
85    @Inject
86    private ModelContext modelContext;
87   
88    /**
89    * The component used to serialize entity references.
90    */
91    @Inject
92    private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;
93   
 
94  26967 toggle @Override
95    public XDOM display(DocumentModelBridge document, DocumentDisplayerParameters parameters)
96    {
97  26967 XDOM xdom = null;
98  26967 if (isSheetExpected(document, parameters)) {
99  25929 Map<String, Object> backupObjects = null;
100  25930 EntityReference currentWikiReference = null;
101  25929 try {
102    // It is very important to determine the sheet in a new, isolated, execution context, if the given
103    // document is not currently on the execution context. Put the given document in the context only if
104    // it's not already there.
105  25934 if (!modelBridge.isCurrentDocument(document)) {
106  10875 backupObjects = modelBridge.pushDocumentInContext(document);
107   
108  10876 currentWikiReference = modelContext.getCurrentEntityReference();
109  10876 modelContext.setCurrentEntityReference(document.getDocumentReference().getWikiReference());
110    }
111  25932 xdom = maybeDisplayWithSheet(document, parameters);
112    } finally {
113  25925 if (backupObjects != null) {
114  10872 documentAccessBridge.popDocumentFromContext(backupObjects);
115  10875 modelContext.setCurrentEntityReference(currentWikiReference);
116    }
117    }
118    }
119   
120    // Fall back on the default document displayer if no sheet was applied. Note that we don't isolate the context
121    // before calling the default document displayer. It is better to let the default document displayer choose if
122    // isolating the execution context is needed due to the way #isSheetExpected() checks for current document.
123  26961 return xdom != null ? xdom : documentDisplayer.display(document, parameters);
124    }
125   
126    /**
127    * @param document the document that is displayed
128    * @param parameters the display parameters
129    * @return {@code true} if the context in which this displayer is called is expecting the document sheet to be
130    * displayed, {@code false} otherwise
131    */
 
132  26965 toggle private boolean isSheetExpected(DocumentModelBridge document, DocumentDisplayerParameters parameters)
133    {
134    // We make 3 checks:
135    //
136    // (1) If the content is not transformed then the goal is probably to include it in a bigger XDOM to be rendered
137    // later, in which case the actual document content is needed, not the content of the sheet.
138    //
139    // (2) If the execution context is not isolated then the document is probably included in another one
140    // (context=current) so we need to display the actual document content. Note that we also test if the given
141    // document is the current one because requested documents (e.g. /view/Space/Page) are displayed from Velocity
142    // templates using $doc.getRenderedContent() which doesn't isolate the execution context. We don't test if the
143    // document instances are the same but if their references are equal because Velocity templates use various
144    // document variables ($doc, $tdoc, $cdoc, etc.) to call getRenderedContent().
145    //
146    // (3) We test if the default edit mode is "edit" to ensure backward compatibility with older XWiki applications
147    // that don't use the new sheet system (they most probably use "inline" as the default edit mode).
148  26965 return parameters.isContentTransformed()
149    && (parameters.isExecutionContextIsolated() || document.getDocumentReference().equals(
150    documentAccessBridge.getCurrentDocumentReference()))
151    && "edit".equals(modelBridge.getDefaultEditMode(document));
152    }
153   
154    /**
155    * Iterates the list of sheets bound to the given document and tries to applies them, stopping after the first
156    * success. If none of the sheets can be applied (e.g. insufficient rights) then it displays the document without a
157    * sheet, using the default document displayer.
158    *
159    * @param document the document that is displayed
160    * @param parameters the display parameters
161    * @return the result of displaying the given document, with or without a sheet
162    */
 
163  25929 toggle private XDOM maybeDisplayWithSheet(DocumentModelBridge document, DocumentDisplayerParameters parameters)
164    {
165  25929 for (DocumentReference sheetReference : getSheetReferences(document)) {
166  2004 if (document.getDocumentReference().equals(sheetReference)) {
167    // If the sheet is the document itself then we simply display the document. We handle this case
168    // differently because unsaved document changes might be ignored if we render the sheet (which is
169    // loaded from the database). So in this case applying the sheet would actually mean displaying the
170    // saved version of the document.
171  9 break;
172  1995 } else if (documentAccessBridge.isDocumentViewable(sheetReference)) {
173  1995 try {
174  1995 return applySheet(document, sheetReference, parameters);
175    } catch (Exception e) {
176  0 String sheetStringReference = defaultEntityReferenceSerializer.serialize(sheetReference);
177  0 logger.warn("Failed to apply sheet [{}].", sheetStringReference, e);
178    }
179    }
180    }
181   
182    // No sheet was applied. Fall back on the default document displayer.
183  23936 return null;
184    }
185   
186    /**
187    * @param document a document
188    * @return the list of sheet references that can be applied to the given document in the current context
189    */
 
190  25930 toggle private List<DocumentReference> getSheetReferences(DocumentModelBridge document)
191    {
192    // XObjects are shared by all document translations and are accessible only from the default translation. We
193    // have to pass the default document translation to the sheet manager because otherwise it won't detect the
194    // sheets.
195  25933 return sheetManager.getSheets(modelBridge.getDefaultTranslation(document), modelBridge.getCurrentAction());
196    }
197   
198    /**
199    * Applies a sheet to a document.
200    *
201    * @param document the document to apply the sheet to
202    * @param sheetReference the sheet to apply
203    * @param parameters the display parameters
204    * @return the result of rendering the sheet in the context of the given document
205    * @throws Exception if applying the sheet fails
206    */
 
207  1995 toggle private XDOM applySheet(DocumentModelBridge document, DocumentReference sheetReference,
208    DocumentDisplayerParameters parameters) throws Exception
209    {
210  1995 DocumentModelBridge sheet = documentAccessBridge.getDocument(sheetReference);
211  1995 if (parameters.isTitleDisplayed() && StringUtils.isEmpty(sheet.getTitle())) {
212    // The sheet doesn't control the title. Fall back on the default document displayer.
213  770 return null;
214    }
215   
216  1225 DocumentModelBridge originalSecurityDoc = this.modelBridge.setSecurityDocument(sheet);
217   
218  1225 try {
219  1225 return display(document, sheet, parameters);
220    } finally {
221  1225 this.modelBridge.setSecurityDocument(originalSecurityDoc);
222    }
223    }
224   
225    /**
226    * Displays a document with a sheet.
227    *
228    * @param document the displayed document
229    * @param sheet the applied sheet
230    * @param parameters the display parameters
231    * @return the result of displaying the sheet in the context of the given document
232    */
 
233  1225 toggle private XDOM display(DocumentModelBridge document, DocumentModelBridge sheet,
234    DocumentDisplayerParameters parameters)
235    {
236  1225 DocumentDisplayerParameters sheetDisplayParameters = parameters.clone();
237    // The execution context was already isolated and we want to display the sheet in the context of the given doc.
238  1225 sheetDisplayParameters.setExecutionContextIsolated(false);
239  1225 return documentDisplayer.display(sheet, sheetDisplayParameters);
240    }
241    }