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

File ContextMacro.java

 

Coverage histogram

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

Code metrics

10
33
3
1
191
106
11
0.33
11
3
3.67

Classes

Class Line # Actions
ContextMacro 59 33 0% 11 5
0.891304489.1%
 

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.context;
21   
22    import java.util.Arrays;
23    import java.util.HashMap;
24    import java.util.List;
25    import java.util.Map;
26   
27    import javax.inject.Inject;
28    import javax.inject.Named;
29    import javax.inject.Singleton;
30   
31    import org.xwiki.bridge.DocumentAccessBridge;
32    import org.xwiki.bridge.DocumentModelBridge;
33    import org.xwiki.component.annotation.Component;
34    import org.xwiki.model.reference.DocumentReference;
35    import org.xwiki.model.reference.DocumentReferenceResolver;
36    import org.xwiki.rendering.block.Block;
37    import org.xwiki.rendering.block.MetaDataBlock;
38    import org.xwiki.rendering.block.XDOM;
39    import org.xwiki.rendering.listener.MetaData;
40    import org.xwiki.rendering.macro.AbstractMacro;
41    import org.xwiki.rendering.macro.MacroContentParser;
42    import org.xwiki.rendering.macro.MacroExecutionException;
43    import org.xwiki.rendering.macro.context.ContextMacroParameters;
44    import org.xwiki.rendering.macro.context.TransformationContextMode;
45    import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;
46    import org.xwiki.rendering.transformation.MacroTransformationContext;
47    import org.xwiki.rendering.transformation.TransformationContext;
48    import org.xwiki.rendering.transformation.TransformationManager;
49   
50    /**
51    * Execute the macro's content in the context of another document's reference.
52    *
53    * @version $Id: 7a71c527601ac4c7436b956d6864fe8e8c224ec9 $
54    * @since 3.0M1
55    */
56    @Component
57    @Named("context")
58    @Singleton
 
59    public class ContextMacro extends AbstractMacro<ContextMacroParameters>
60    {
61    /**
62    * The description of the macro.
63    */
64    private static final String DESCRIPTION = "Executes content in the context of the passed document";
65   
66    /**
67    * The description of the macro content.
68    */
69    private static final String CONTENT_DESCRIPTION = "The content to execute";
70   
71    /**
72    * Used to set the current document in the context (old way) and check rights.
73    */
74    @Inject
75    private DocumentAccessBridge documentAccessBridge;
76   
77    /**
78    * The parser used to parse macro content.
79    */
80    @Inject
81    private MacroContentParser contentParser;
82   
83    /**
84    * Used to transform document links into absolute references.
85    */
86    @Inject
87    @Named("macro")
88    private DocumentReferenceResolver<String> macroDocumentReferenceResolver;
89   
90    @Inject
91    private TransformationManager transformationManager;
92   
93    /**
94    * Create and initialize the descriptor of the macro.
95    */
 
96  6 toggle public ContextMacro()
97    {
98  6 super("Context", DESCRIPTION, new DefaultContentDescriptor(CONTENT_DESCRIPTION), ContextMacroParameters.class);
99   
100    // The Context macro must execute early since it can contain include macros which can bring stuff like headings
101    // for other macros (TOC macro, etc). Make it the same priority as the Include macro.
102  6 setPriority(10);
103  6 setDefaultCategory(DEFAULT_CATEGORY_DEVELOPMENT);
104    }
105   
 
106  0 toggle @Override
107    public boolean supportsInlineMode()
108    {
109  0 return true;
110    }
111   
 
112  6 toggle @Override
113    public List<Block> execute(ContextMacroParameters parameters, String content, MacroTransformationContext context)
114    throws MacroExecutionException
115    {
116  6 if (parameters.getDocument() == null) {
117  1 throw new MacroExecutionException("You must specify a 'document' parameter pointing to the document to "
118    + "set in the context as the current document.");
119    }
120   
121  5 DocumentReference referencedDocReference =
122    this.macroDocumentReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
123   
124  5 boolean currentContextHasProgrammingRights = this.documentAccessBridge.hasProgrammingRights();
125   
126  5 List<Block> result;
127  5 try {
128  5 Map<String, Object> backupObjects = new HashMap<>();
129  5 try {
130  5 this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference);
131   
132    // The current document is now the passed document. Check for programming rights for it. If it has
133    // programming rights then the initial current document also needs programming right, else throw an
134    // error since it would be a security breach otherwise.
135  5 if (this.documentAccessBridge.hasProgrammingRights() && !currentContextHasProgrammingRights) {
136  1 throw new MacroExecutionException("Current document must have programming rights since the "
137    + "context document provided [" + parameters.getDocument() + "] has programming rights.");
138    }
139   
140  4 MetaData metadata = new MetaData();
141  4 metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());
142  4 metadata.addMetaData(MetaData.BASE, parameters.getDocument());
143   
144  4 XDOM xdom = this.contentParser.parse(content, context, false, metadata, false);
145   
146    // Configure the Transformation Context depending on the mode asked.
147  4 if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT
148    || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS)
149    {
150    // Apply the transformations but with a Transformation Context having the XDOM of the passed
151    // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate
152    // the toc for the passed document instead of the current document).
153  1 DocumentModelBridge referencedDoc = this.documentAccessBridge.getDocument(referencedDocReference);
154  1 XDOM referencedXDOM = referencedDoc.getXDOM();
155   
156  1 if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) {
157    // Get the XDOM from the referenced doc but with Transformations applied so that all macro are
158    // executed and contribute XDOM elements.
159    // IMPORTANT: This can be dangerous since it means executing macros, and thus also script macros
160    // defined in the referenced document. To be used with caution.
161  1 TransformationContext referencedTxContext =
162    new TransformationContext(referencedXDOM, referencedDoc.getSyntax());
163  1 this.transformationManager.performTransformations(referencedXDOM, referencedTxContext);
164    }
165   
166    // Now execute transformation on the context macro content but with the referenced XDOM in the
167    // Transformation context!
168  1 TransformationContext txContext =
169    new TransformationContext(referencedXDOM, referencedDoc.getSyntax());
170  1 this.transformationManager.performTransformations(xdom, txContext);
171    }
172   
173    // Keep metadata so that the result stay associated to context properties when inserted in the parent
174    // XDOM
175  4 result = Arrays.asList((Block) new MetaDataBlock(xdom.getChildren(), xdom.getMetaData()));
176   
177    } finally {
178  5 this.documentAccessBridge.popDocumentFromContext(backupObjects);
179    }
180    } catch (Exception e) {
181  1 if (e instanceof MacroExecutionException) {
182  1 throw (MacroExecutionException) e;
183    } else {
184  0 throw new MacroExecutionException(
185    String.format("Failed to render page in the context of [%s]", referencedDocReference), e);
186    }
187    }
188   
189  4 return result;
190    }
191    }