Exemple #1
0
  @Override
  public List<Block> execute(Object parameters, String content, MacroTransformationContext context)
      throws MacroExecutionException {
    XDOM parsedDom;
    // get a parser for the desired syntax identifier
    Parser parser = getSyntaxParser(context.getSyntax().toIdString());

    try {
      // parse the content of the wiki macro that has been injected by the component manager the
      // content of the macro call itself is ignored.
      parsedDom = parser.parse(new StringReader(content));
    } catch (ParseException e) {
      throw new MacroExecutionException(
          "Failed to parse content ["
              + content
              + "] with Syntax parser ["
              + parser.getSyntax()
              + "]",
          e);
    }

    List<MacroBlock> potentialCells = parsedDom.getChildrenByType(MacroBlock.class, false);
    int count = this.countCells(potentialCells);
    if (count == 0) {
      throw new MacroExecutionException(
          "TableRow macro expect at least one cell macro as first-level children");
    }

    // Make the actual cells, injecting <td> tags around cell macros
    this.makeCells(potentialCells);

    return Collections.singletonList((Block) parsedDom);
  }
  @Test(expected = MacroExecutionException.class)
  public void testRestrictedByContext() throws Exception {
    VelocityMacroParameters params = new VelocityMacroParameters();
    MacroTransformationContext context = new MacroTransformationContext();
    context.setSyntax(Syntax.XWIKI_2_0);
    context.setCurrentMacroBlock(
        new MacroBlock("velocity", Collections.<String, String>emptyMap(), false));
    context.setId("page1");

    // Restrict the transformation context.
    context.getTransformationContext().setRestricted(true);

    when(authorizationManager.hasAccess(Right.SCRIPT)).thenReturn(true);

    mocker
        .getComponentUnderTest()
        .execute(params, "#macro(testMacrosAreLocal)mymacro#end", context);
  }
  /** {@inheritDoc} */
  public List<Block> execute(
      SectionMacroParameters parameters, String content, MacroTransformationContext context)
      throws MacroExecutionException {
    XDOM parsedDom;

    // get a parser for the desired syntax identifier
    Parser parser = getSyntaxParser(context.getSyntax().toIdString());

    try {
      // parse the content of the wiki macro that has been injected by the
      // component manager
      // the content of the macro call itself is ignored.
      parsedDom = parser.parse(new StringReader(content));
    } catch (ParseException e) {
      throw new MacroExecutionException(
          "Failed to parse content ["
              + content
              + "] with Syntax parser ["
              + parser.getSyntax()
              + "]",
          e);
    }

    List<MacroBlock> potentialColumns = parsedDom.getChildrenByType(MacroBlock.class, false);

    int count = this.countColumns(potentialColumns);

    if (count == 0) {
      throw new MacroExecutionException(
          "Section macro expect at least one column macro as first-level children");
    }

    double computedColumnWidth = ((TOTAL_WIDTH - COLUMN_RIGHT_PADDING_RATE * (count - 1)) / count);

    // Make the actual columns injecting divs around column macros
    this.makeColumns(potentialColumns, computedColumnWidth);

    // finally, clear the floats introduced by the columns
    Map<String, String> clearFloatsParams = new HashMap<String, String>();
    clearFloatsParams.put(PARAMETER_STYLE, STYLE_CLEAR_BOTH);

    parsedDom.addChild(new GroupBlock(clearFloatsParams));

    Map<String, String> sectionParameters = new HashMap<String, String>();
    if (parameters.isJustify()) {
      sectionParameters.put(PARAMETER_STYLE, STYLE_TEXT_ALIGN_JUSTIFY);
    }

    Block sectionRoot = new GroupBlock(sectionParameters);
    sectionRoot.addChildren(parsedDom.getChildren());

    return Collections.singletonList(sectionRoot);
  }
 /**
  * Method to overwrite to indicate the script engine name.
  *
  * @param parameters the macro parameters.
  * @param context the context of the macro transformation.
  * @return the name of the script engine to use.
  */
 protected String getScriptEngineName(P parameters, MacroTransformationContext context) {
   return context.getCurrentMacroBlock().getId().toLowerCase();
 }
  @Override
  public void transform(Block rootBlock, TransformationContext context)
      throws TransformationException {
    // Create a macro execution context with all the information required for macros.
    MacroTransformationContext macroContext = new MacroTransformationContext(context);
    macroContext.setTransformation(this);

    // Counter to prevent infinite recursion if a macro generates the same macro for example.
    for (int recursions = 0; recursions < this.maxRecursions; ) {
      // 1) Get highest priority macro
      PriorityMacroBlockMatcher priorityMacroBlockMatcher =
          new PriorityMacroBlockMatcher(context.getSyntax());
      rootBlock.getFirstBlock(priorityMacroBlockMatcher, Block.Axes.DESCENDANT);

      // 2) Apply macros lookup errors
      if (priorityMacroBlockMatcher.errors != null) {
        for (MacroLookupExceptionElement error : priorityMacroBlockMatcher.errors) {
          if (error.exception instanceof MacroNotFoundException) {
            // Macro cannot be found. Generate an error message instead of the macro execution
            // result.
            // TODO: make it internationalized
            this.macroErrorManager.generateError(
                error.macroBlock,
                String.format("Unknown macro: %s.", error.macroBlock.getId()),
                String.format(
                    "The \"%s\" macro is not in the list of registered macros. Verify the spelling or "
                        + "contact your administrator.",
                    error.macroBlock.getId()));
            this.logger.debug(
                "Failed to locate the [{}] macro. Ignoring it.", error.macroBlock.getId());
          } else {
            // TODO: make it internationalized
            this.macroErrorManager.generateError(
                error.macroBlock,
                String.format("Invalid macro: %s.", error.macroBlock.getId()),
                error.exception);
            this.logger.debug(
                "Failed to instantiate the [{}] macro. Ignoring it.", error.macroBlock.getId());
          }
        }
      }

      MacroBlock macroBlock = priorityMacroBlockMatcher.block;

      if (macroBlock == null) {
        // Nothing left to do
        return;
      }

      Macro<?> macro = priorityMacroBlockMatcher.blockMacro;

      boolean incrementRecursions = macroBlock.getParent() instanceof MacroMarkerBlock;

      List<Block> newBlocks;
      try {
        // 3) Verify if we're in macro inline mode and if the macro supports it. If not, send an
        // error.
        if (macroBlock.isInline()) {
          macroContext.setInline(true);
          if (!macro.supportsInlineMode()) {
            // The macro doesn't support inline mode, raise a warning but continue.
            // The macro will not be executed and we generate an error message instead of the macro
            // execution result.
            this.macroErrorManager.generateError(
                macroBlock,
                "This is a standalone macro only and it cannot be used inline",
                "This macro generates standalone content. As a consequence you need to make sure to use a "
                    + "syntax that separates your macro from the content before and after it so that it's on a "
                    + "line by itself. For example in XWiki Syntax 2.0+ this means having 2 newline characters "
                    + "(a.k.a line breaks) separating your macro from the content before and after it.");
            this.logger.debug("The [{}] macro doesn't support inline mode.", macroBlock.getId());

            continue;
          }
        } else {
          macroContext.setInline(false);
        }

        // 4) Execute the highest priority macro
        macroContext.setCurrentMacroBlock(macroBlock);
        ((MutableRenderingContext) this.renderingContext).setCurrentBlock(macroBlock);

        // Populate and validate macro parameters.
        Object macroParameters = macro.getDescriptor().getParametersBeanClass().newInstance();
        try {
          this.beanManager.populate(macroParameters, macroBlock.getParameters());
        } catch (Throwable e) {
          // One macro parameter was invalid.
          // The macro will not be executed and we generate an error message instead of the macro
          // execution result.
          this.macroErrorManager.generateError(
              macroBlock,
              String.format(
                  "Invalid macro parameters used for the \"%s\" macro.", macroBlock.getId()),
              e);
          this.logger.debug(
              "Invalid macro parameter for the [{}] macro. Internal error: [{}].",
              macroBlock.getId(),
              e.getMessage());

          continue;
        }

        newBlocks = ((Macro) macro).execute(macroParameters, macroBlock.getContent(), macroContext);
      } catch (Throwable e) {
        // The Macro failed to execute.
        // The macro will not be executed and we generate an error message instead of the macro
        // execution result.
        // Note: We catch any Exception because we want to never break the whole rendering.
        this.macroErrorManager.generateError(
            macroBlock, String.format("Failed to execute the [%s] macro.", macroBlock.getId()), e);
        this.logger.debug(
            "Failed to execute the [{}] macro. Internal error [{}].",
            macroBlock.getId(),
            e.getMessage());

        continue;
      } finally {
        ((MutableRenderingContext) this.renderingContext).setCurrentBlock(null);
      }

      // We wrap the blocks generated by the macro execution with MacroMarker blocks so that
      // listeners/renderers
      // who wish to know the group of blocks that makes up the executed macro can. For example this
      // is useful for
      // the XWiki Syntax renderer so that it can reconstruct the macros from the transformed XDOM.
      Block resultBlock = wrapInMacroMarker(macroBlock, newBlocks);

      // 5) Replace the MacroBlock by the Blocks generated by the execution of the Macro
      macroBlock.getParent().replaceChild(resultBlock, macroBlock);

      if (incrementRecursions) {
        ++recursions;
      }
    }
  }
  @Override
  public List<Block> execute(
      IncludeMacroParameters parameters, String content, MacroTransformationContext context)
      throws MacroExecutionException {
    // Step 1: Perform checks.
    if (parameters.getReference() == null) {
      throw new MacroExecutionException(
          "You must specify a 'reference' parameter pointing to the entity to include.");
    }

    DocumentReference includedReference = resolve(context.getCurrentMacroBlock(), parameters);

    checkRecursiveInclusion(context.getCurrentMacroBlock(), includedReference);

    if (!this.documentAccessBridge.isDocumentViewable(includedReference)) {
      throw new MacroExecutionException(
          String.format(
              "Current user [%s] doesn't have view rights on document [%s]",
              this.documentAccessBridge.getCurrentUserReference(),
              this.defaultEntityReferenceSerializer.serialize(includedReference)));
    }

    Context parametersContext = parameters.getContext();

    // Step 2: Retrieve the included document.
    DocumentModelBridge documentBridge;
    try {
      documentBridge = this.documentAccessBridge.getDocument(includedReference);
    } catch (Exception e) {
      throw new MacroExecutionException(
          "Failed to load Document ["
              + this.defaultEntityReferenceSerializer.serialize(includedReference)
              + "]",
          e);
    }

    // Step 3: Display the content of the included document.

    // Check the value of the "context" parameter.
    //
    // If CONTEXT_NEW then display the content in an isolated execution and transformation context.
    //
    // if CONTEXT_CURRENT then display the content without performing any transformations (we don't
    // want any Macro
    // to be executed at this stage since they should be executed by the currently running Macro
    // Transformation.
    DocumentDisplayerParameters displayParameters = new DocumentDisplayerParameters();
    displayParameters.setContentTransformed(parametersContext == Context.NEW);
    displayParameters.setExecutionContextIsolated(displayParameters.isContentTransformed());
    displayParameters.setSectionId(parameters.getSection());
    displayParameters.setTransformationContextIsolated(displayParameters.isContentTransformed());
    displayParameters.setTransformationContextRestricted(
        context.getTransformationContext().isRestricted());
    displayParameters.setTargetSyntax(context.getTransformationContext().getTargetSyntax());

    Stack<Object> references = this.inclusionsBeingExecuted.get();
    if (parametersContext == Context.NEW) {
      if (references == null) {
        references = new Stack<Object>();
        this.inclusionsBeingExecuted.set(references);
      }
      references.push(includedReference);
    }

    XDOM result;
    try {
      result = this.documentDisplayer.display(documentBridge, displayParameters);
    } catch (Exception e) {
      throw new MacroExecutionException(e.getMessage(), e);
    } finally {
      if (parametersContext == Context.NEW) {
        references.pop();
      }
    }

    // Step 4: Wrap Blocks in a MetaDataBlock with the "source" meta data specified so that we know
    // from where the
    // content comes and "base" meta data so that reference are properly resolved
    MetaDataBlock metadata = new MetaDataBlock(result.getChildren(), result.getMetaData());
    String source = this.defaultEntityReferenceSerializer.serialize(includedReference);
    metadata.getMetaData().addMetaData(MetaData.SOURCE, source);
    if (parametersContext == Context.NEW) {
      metadata.getMetaData().addMetaData(MetaData.BASE, source);
    }

    return Arrays.<Block>asList(metadata);
  }