/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.rendering.internal.macro.chart.source.table; import java.util.List; import javax.inject.Inject; import javax.inject.Named; import org.slf4j.Logger; import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.bridge.DocumentModelBridge; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.display.internal.DocumentDisplayer; import org.xwiki.display.internal.DocumentDisplayerParameters; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.rendering.block.Block; import org.xwiki.rendering.block.MacroBlock; import org.xwiki.rendering.block.MetaDataBlock; import org.xwiki.rendering.block.TableBlock; import org.xwiki.rendering.block.XDOM; import org.xwiki.rendering.block.match.ClassBlockMatcher; import org.xwiki.rendering.block.match.MetadataBlockMatcher; import org.xwiki.rendering.listener.MetaData; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.transformation.MacroTransformationContext; import org.xwiki.security.authorization.AuthorizationManager; import org.xwiki.security.authorization.Right; import static org.xwiki.component.descriptor.ComponentInstantiationStrategy.PER_LOOKUP; /** * A data source that allows building charts from {@link XDOM} sources. * * @version $Id: e7023430a9c9824e557eaa3f100958fe8d7ac4e5 $ * @since 4.2M1 */ @Component @Named("xdom") @InstantiationStrategy(PER_LOOKUP) public class DocumentTableBlockDataSource extends AbstractTableBlockDataSource { /** * Identifies which xdom to process. */ private static final String DOCUMENT_PARAM = "document"; /** * Identifies the table on the xdom. */ private static final String TABLE_PARAM = "table"; /** * The document name of the document holding the table. If null then the data source is located in the current * document. */ private DocumentReference documentReference; /** * The id of the table holding the data. */ private String tableId; /** * A logger. */ @Inject private Logger logger; /** * {@link DocumentDisplayer} used for rendering the document contents. */ @Inject private DocumentDisplayer documentDisplayer; /** * {@link DocumentAccessBridge} component. */ @Inject private DocumentAccessBridge docBridge; /** * A document reference resolver. */ @Inject private DocumentReferenceResolver<String> documentReferenceResolver; /** * The authorization manager. */ @Inject private AuthorizationManager authorizationManager; @Override protected TableBlock getTableBlock(String macroContent, MacroTransformationContext context) throws MacroExecutionException { XDOM xdom = computeXDOM(context); // Find the correct table block. List<TableBlock> tableBlocks = xdom.getBlocks(new ClassBlockMatcher(TableBlock.class), Block.Axes.DESCENDANT); TableBlock result = null; this.logger.debug("Table id is [{}], there are [{}] tables in the document [{}]", new Object[]{this.tableId, tableBlocks.size(), this.documentReference}); if (null != tableId) { for (TableBlock tableBlock : tableBlocks) { String id = tableBlock.getParameter("id"); if (null != id && id.equals(this.tableId)) { result = tableBlock; break; } } } else { result = (tableBlocks.size() > 0) ? tableBlocks.get(0) : null; } if (null == result) { throw new MacroExecutionException("Unable to find a matching data table."); } return result; } /** * Get the XDOM for the data source. * * @param context the Macro context from which we can get the XDOM if the source is in the current content * @return the XDOM in which the data source is located * @throws MacroExecutionException in case of an error getting the XDOM */ private XDOM computeXDOM(MacroTransformationContext context) throws MacroExecutionException { XDOM xdom; // Parse the document content into an XDOM. If the reference is to the current document then we should not // Parse the content again since 1) that's unnecessary since we can hold of the XDOM from the Transformation // Context and 2) it's going to cause a cycle... if (isDefinedChartSourceTheCurrentDocument(context.getCurrentMacroBlock())) { xdom = context.getXDOM(); } else { try { DocumentModelBridge document = this.docBridge.getDocument(this.documentReference); DocumentDisplayerParameters parameters = new DocumentDisplayerParameters(); parameters.setContentTranslated(true); parameters.setTargetSyntax(context.getTransformationContext().getTargetSyntax()); xdom = this.documentDisplayer.display(document, parameters); } catch (Exception e) { throw new MacroExecutionException(String.format("Error getting Chart table from document [%s]", this.documentReference, e)); } } return xdom; } /** * @param currentMacroBlock the current macro block being rendered * @return true if the chart macro takes its source in the current document or false otherwise */ protected boolean isDefinedChartSourceTheCurrentDocument(MacroBlock currentMacroBlock) { boolean result; if (this.documentReference == null) { result = true; } else { String sourceReference = extractSourceContentReference(currentMacroBlock); if (this.documentReferenceResolver.resolve(sourceReference, this.docBridge.getCurrentDocumentReference()).equals(this.documentReference)) { result = true; } else { result = false; } } return result; } @Override protected boolean setParameter(String key, String value) throws MacroExecutionException { if (DOCUMENT_PARAM.equals(key)) { this.documentReference = this.documentReferenceResolver.resolve(value, docBridge.getCurrentDocumentReference()); return true; } if (TABLE_PARAM.equals(key)) { this.tableId = value; return true; } return super.setParameter(key, value); } @Override protected void validateParameters() throws MacroExecutionException { super.validateParameters(); if (this.documentReference != null) { if (!authorizationManager.hasAccess(Right.VIEW, this.docBridge.getCurrentUserReference(), this.documentReference)) { throw new MacroExecutionException("You do not have permission to view the document."); } if (!this.docBridge.exists(this.documentReference)) { throw new MacroExecutionException( String.format("Document [%s] does not exist.", this.documentReference)); } } } /** * @param source the blocks from where to try to extract the source content * @return the source content reference or null if none is found */ private String extractSourceContentReference(Block source) { String contentSource = null; MetaDataBlock metaDataBlock = source.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Block.Axes.ANCESTOR); if (metaDataBlock != null) { contentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE); } return contentSource; } }