@Test public void testRegisterWikiMacroWhenGlobalVisibilityAndAllowed() throws Exception { final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.GLOBAL); // Simulate a user with programming rights getMockery() .checking( new Expectations() { { allowing(mockDocumentAccessBridge).getCurrentWiki(); will(returnValue("wiki")); allowing(mockDocumentAccessBridge).getCurrentUser(); will(returnValue("dummy")); allowing(mockDocumentAccessBridge).setCurrentUser("dummy"); allowing(mockWikiMacroFactory) .isAllowed(wikiMacro.getDocumentReference(), WikiMacroVisibility.GLOBAL); will(returnValue(true)); } }); Assert.assertFalse(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentReference())); wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentReference(), wikiMacro); Assert.assertTrue(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentReference())); Macro<?> registeredMacro = macroManager.getMacro(new MacroId("testwikimacro")); Assert.assertEquals(0, registeredMacro.compareTo(wikiMacro)); wikiMacroManager.unregisterWikiMacro(wikiMacro.getDocumentReference()); Assert.assertFalse(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentReference())); Assert.assertFalse(macroManager.exists(new MacroId("testwikimacro"))); }
@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; } } }