1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.rendering.internal.macro.display

File DisplayMacro.java

 
testDisplayMacroWhenSectionSpecified: Failed to lookup default document displayer.
testDisplayMacroWhenDisplayingDocumentWithRelativeReferences: Failed to lookup default document displayer.
 

Coverage histogram

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

Code metrics

12
46
6
1
227
131
15
0.33
7.67
6
2.5

Classes

Class Line # Actions
DisplayMacro 59 46 0% 15 5
0.92187592.2%
 

Contributing tests

This file is covered by 6 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.rendering.internal.macro.display;
21   
22    import java.util.Collections;
23    import java.util.List;
24    import java.util.Stack;
25   
26    import javax.inject.Inject;
27    import javax.inject.Named;
28    import javax.inject.Singleton;
29   
30    import org.xwiki.bridge.DocumentAccessBridge;
31    import org.xwiki.bridge.DocumentModelBridge;
32    import org.xwiki.component.annotation.Component;
33    import org.xwiki.display.internal.DocumentDisplayer;
34    import org.xwiki.display.internal.DocumentDisplayerParameters;
35    import org.xwiki.model.reference.DocumentReference;
36    import org.xwiki.model.reference.EntityReference;
37    import org.xwiki.model.reference.EntityReferenceResolver;
38    import org.xwiki.model.reference.EntityReferenceSerializer;
39    import org.xwiki.rendering.block.Block;
40    import org.xwiki.rendering.block.MacroBlock;
41    import org.xwiki.rendering.block.MetaDataBlock;
42    import org.xwiki.rendering.block.XDOM;
43    import org.xwiki.rendering.listener.MetaData;
44    import org.xwiki.rendering.macro.AbstractMacro;
45    import org.xwiki.rendering.macro.MacroExecutionException;
46    import org.xwiki.rendering.macro.display.DisplayMacroParameters;
47    import org.xwiki.rendering.transformation.MacroTransformationContext;
48    import org.xwiki.security.authorization.ContextualAuthorizationManager;
49    import org.xwiki.security.authorization.Right;
50   
51    /**
52    * @version $Id: 264a5ad776fd193e5a698e12965c727e51c732a2 $
53    * @since 3.4M1
54    */
55    // TODO: add support for others entity types (not only document). Mainly require more generic displayer API.
56    @Component
57    @Named("display")
58    @Singleton
 
59    public class DisplayMacro extends AbstractMacro<DisplayMacroParameters>
60    {
61    /**
62    * The description of the macro.
63    */
64    private static final String DESCRIPTION = "Display other pages into the current page.";
65   
66    /**
67    * Used to access document content and check view access right.
68    */
69    @Inject
70    private DocumentAccessBridge documentAccessBridge;
71   
72    @Inject
73    private ContextualAuthorizationManager authorization;
74   
75    /**
76    * Used to transform the passed document reference macro parameter into a typed {@link DocumentReference} object.
77    */
78    @Inject
79    @Named("macro")
80    private EntityReferenceResolver<String> macroEntityReferenceResolver;
81   
82    /**
83    * Used to serialize resolved document links into a string again since the Rendering API only manipulates Strings
84    * (done voluntarily to be independent of any wiki engine and not draw XWiki-specific dependencies).
85    */
86    @Inject
87    private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;
88   
89    /**
90    * Used to display the content of the included document.
91    */
92    @Inject
93    @Named("configured")
94    private DocumentDisplayer documentDisplayer;
95   
96    /**
97    * A stack of all currently executing include macros with context=new for catching recursive inclusion.
98    */
99    private ThreadLocal<Stack<Object>> displaysBeingExecuted = new ThreadLocal<>();
100   
101    /**
102    * Default constructor.
103    */
 
104  10 toggle public DisplayMacro()
105    {
106  10 super("Display", DESCRIPTION, DisplayMacroParameters.class);
107   
108    // The include macro must execute first since if it runs with the current context it needs to bring
109    // all the macros from the included page before the other macros are executed.
110  10 setPriority(10);
111  10 setDefaultCategory(DEFAULT_CATEGORY_CONTENT);
112    }
113   
 
114  10 toggle @Override
115    public boolean supportsInlineMode()
116    {
117  10 return true;
118    }
119   
120    /**
121    * Allows overriding the Document Access Bridge used (useful for unit tests).
122    *
123    * @param documentAccessBridge the new Document Access Bridge to use
124    */
 
125  3 toggle public void setDocumentAccessBridge(DocumentAccessBridge documentAccessBridge)
126    {
127  3 this.documentAccessBridge = documentAccessBridge;
128    }
129   
 
130  18 toggle @Override
131    public List<Block> execute(DisplayMacroParameters parameters, String content, MacroTransformationContext context)
132    throws MacroExecutionException
133    {
134    // Step 1: Perform checks.
135  18 if (parameters.getReference() == null) {
136  1 throw new MacroExecutionException(
137    "You must specify a 'reference' parameter pointing to the entity to display.");
138    }
139   
140  17 EntityReference includedReference = resolve(context.getCurrentMacroBlock(), parameters);
141   
142  17 checkRecursiveDisplay(includedReference);
143   
144    // Step 2: Retrieve the included document.
145  16 DocumentModelBridge documentBridge;
146  16 try {
147  16 documentBridge = this.documentAccessBridge.getDocumentInstance(includedReference);
148    } catch (Exception e) {
149  0 throw new MacroExecutionException(
150    "Failed to load Document [" + this.defaultEntityReferenceSerializer.serialize(includedReference) + "]",
151    e);
152    }
153   
154    // Step 3: Check right
155  16 if (!this.authorization.hasAccess(Right.VIEW, documentBridge.getDocumentReference())) {
156  0 throw new MacroExecutionException(
157    String.format("Current user [%s] doesn't have view rights on document [%s]",
158    this.documentAccessBridge.getCurrentUserReference(), includedReference));
159    }
160   
161    // Step 4: Display the content of the included document.
162    // Display the content in an isolated execution and transformation context.
163  16 DocumentDisplayerParameters displayParameters = new DocumentDisplayerParameters();
164  16 displayParameters.setContentTransformed(true);
165  16 displayParameters.setExecutionContextIsolated(displayParameters.isContentTransformed());
166  16 displayParameters.setSectionId(parameters.getSection());
167  16 displayParameters.setTransformationContextIsolated(displayParameters.isContentTransformed());
168  16 displayParameters.setTargetSyntax(context.getTransformationContext().getTargetSyntax());
169  16 displayParameters.setContentTranslated(true);
170   
171  16 Stack<Object> references = this.displaysBeingExecuted.get();
172  16 if (references == null) {
173  12 references = new Stack<>();
174  12 this.displaysBeingExecuted.set(references);
175    }
176  16 references.push(includedReference);
177   
178  16 XDOM result;
179  16 try {
180  16 Test failure here result = this.documentDisplayer.display(documentBridge, displayParameters);
181    } catch (Exception e) {
182  5 Test failure here throw new MacroExecutionException(e.getMessage(), e);
183    } finally {
184  16 references.pop();
185  16 if (references.isEmpty()) {
186    // Get rid of the current ThreadLocal if not needed anymore
187  12 this.displaysBeingExecuted.remove();
188    }
189    }
190   
191    // Step 5: Wrap Blocks in a MetaDataBlock with the "source" meta data specified so that we know from where the
192    // content comes and "base" meta data so that reference are properly resolved
193  11 MetaDataBlock metadata = new MetaDataBlock(result.getChildren(), result.getMetaData());
194  11 String source = this.defaultEntityReferenceSerializer.serialize(includedReference);
195  11 metadata.getMetaData().addMetaData(MetaData.SOURCE, source);
196  11 metadata.getMetaData().addMetaData(MetaData.BASE, source);
197   
198  11 return Collections.singletonList(metadata);
199    }
200   
201    /**
202    * Protect form recursive display.
203    *
204    * @param reference the reference of the document being included
205    * @throws MacroExecutionException recursive inclusion has been found
206    */
 
207  17 toggle private void checkRecursiveDisplay(EntityReference reference) throws MacroExecutionException
208    {
209    // Try to find recursion in the thread
210  17 Stack<Object> references = this.displaysBeingExecuted.get();
211  17 if (references != null && references.contains(reference)) {
212  1 throw new MacroExecutionException("Found recursive display of document [" + reference + "]");
213    }
214    }
215   
 
216  17 toggle private EntityReference resolve(MacroBlock block, DisplayMacroParameters parameters) throws MacroExecutionException
217    {
218  17 String reference = parameters.getReference();
219   
220  17 if (reference == null) {
221  0 throw new MacroExecutionException(
222    "You must specify a 'reference' parameter pointing to the entity to include.");
223    }
224   
225  17 return this.macroEntityReferenceResolver.resolve(reference, parameters.getType(), block);
226    }
227    }