1. Project Clover database Tue Dec 20 2016 21:24:09 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/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

10
43
6
1
218
124
14
0.33
7.17
6
2.33

Classes

Class Line # Actions
DisplayMacro 56 43 0% 14 13
0.77966178%
 

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