Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* 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.icon.macro.internal;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.bridge.DocumentModelBridge;
import org.xwiki.bridge.internal.DocumentContextExecutor;
import org.xwiki.component.annotation.Component;
import org.xwiki.icon.IconException;
import org.xwiki.icon.IconRenderer;
import org.xwiki.icon.IconSet;
import org.xwiki.icon.IconSetManager;
import org.xwiki.icon.macro.DisplayIconMacroParameters;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.rendering.async.internal.AbstractExecutedContentMacro;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.rendering.listener.MetaData;
import org.xwiki.rendering.macro.MacroExecutionException;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.rendering.transformation.MacroTransformationContext;
import org.xwiki.security.authorization.ContextualAuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.user.UserReferenceSerializer;

/**
* Macro for displaying an icon.
*
* @version $Id$
* @since 14.10.6
* @since 15.2RC1
*/
@Component
@Named("displayIcon")
@Singleton
public class DisplayIconMacro extends AbstractExecutedContentMacro<DisplayIconMacroParameters>
{
private static final String DESCRIPTION = "Display an icon.";

@Inject
private IconSetManager iconSetManager;

@Inject
private IconRenderer iconRenderer;

@Inject
private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;

@Inject
private ContextualAuthorizationManager contextualAuthorization;

@Inject
@Named("document")
private UserReferenceSerializer<DocumentReference> documentUserSerializer;

@Inject
private DocumentContextExecutor documentContextExecutor;

/**
* Default constructor.
*/
public DisplayIconMacro()
{
super("Icon", DESCRIPTION, null, DisplayIconMacroParameters.class);

setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT));
}

@Override
public List<Block> execute(DisplayIconMacroParameters parameters, String content,
MacroTransformationContext context) throws MacroExecutionException
{
List<Block> result;

try {
IconSet iconSet = getIconSet(parameters);

if (iconSet == null) {
result = List.of();
} else {
XDOM iconBlock = parseIcon(parameters, context, iconSet);

BlockAsyncRendererConfiguration rendererConfiguration =
createBlockAsyncRendererConfiguration(null, iconBlock, null, context);
rendererConfiguration.setAsyncAllowed(false);
rendererConfiguration.setCacheAllowed(false);

if (iconSet.getSourceDocumentReference() != null) {
DocumentReference sourceDocumentReference = iconSet.getSourceDocumentReference();

DocumentModelBridge sourceDocument =
this.documentAccessBridge.getDocumentInstance(sourceDocumentReference);
DocumentReference authorReference =
this.documentUserSerializer.serialize(sourceDocument.getAuthors().getContentAuthor());

rendererConfiguration.setSecureReference(sourceDocumentReference, authorReference);
rendererConfiguration.useEntity(sourceDocumentReference);

String stringDocumentReference =
this.defaultEntityReferenceSerializer.serialize(iconSet.getSourceDocumentReference());
rendererConfiguration.setTransformationId(stringDocumentReference);
rendererConfiguration.setResricted(false);

result = this.documentContextExecutor.call(
() -> List.of(this.executor.execute(rendererConfiguration)),
sourceDocument
);
} else {
result = List.of(this.executor.execute(rendererConfiguration));
}
}
} catch (MacroExecutionException e) {
throw e;
} catch (Exception e) {
throw new MacroExecutionException("Failed parsing and executing the icon.", e);
}

return result;
}

private XDOM parseIcon(DisplayIconMacroParameters parameters, MacroTransformationContext context, IconSet iconSet)
throws IconException, MacroExecutionException
{
String iconContent = this.iconRenderer.render(parameters.getName(), iconSet);
MetaData metaData = null;

if (iconSet.getSourceDocumentReference() != null) {
String stringReference =
this.defaultEntityReferenceSerializer.serialize(iconSet.getSourceDocumentReference());
metaData = new MetaData(Map.of(MetaData.SOURCE, stringReference));
}

return this.parser.parse(iconContent, Syntax.XWIKI_2_1, context, false, metaData,
context.isInline());
}

private IconSet getIconSet(DisplayIconMacroParameters parameters) throws IconException, MacroExecutionException
{
IconSet iconSet;

if (parameters.getIconSet() == null) {
iconSet = this.iconSetManager.getCurrentIconSet();
} else {
iconSet = this.iconSetManager.getIconSet(parameters.getIconSet());
}

// Check if the current user can access the icon theme. If not, fall back to the default icon theme or throw
// an exception when the fallback is disabled.
if (iconSet != null && iconSet.getSourceDocumentReference() != null
&& !this.contextualAuthorization.hasAccess(Right.VIEW, iconSet.getSourceDocumentReference()))
{
if (parameters.isFallback()) {
iconSet = null;
} else {
throw new MacroExecutionException(
String.format("Current user [%s] doesn't have view rights on the icon set's document [%s]",
this.documentAccessBridge.getCurrentUserReference(), iconSet.getSourceDocumentReference()));
}
}

if (parameters.isFallback() && (iconSet == null || !iconSet.hasIcon(parameters.getName()))) {
iconSet = this.iconSetManager.getDefaultIconSet();
}

return iconSet;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# ---------------------------------------------------------------------------
# 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.
# ---------------------------------------------------------------------------
rendering.macro.displayIcon.name=Icon
rendering.macro.displayIcon.description=Display an icon.
rendering.macro.displayIcon.parameter.name.name=Name
rendering.macro.displayIcon.parameter.name.description=The name of the icon.
rendering.macro.displayIcon.parameter.iconSet.name=Icon Set
rendering.macro.displayIcon.parameter.iconSet.description=The name of the icon set.
rendering.macro.displayIcon.parameter.fallback.name=Fallback
rendering.macro.displayIcon.parameter.fallback.description=If the icon shall be loaded from the default icon set when \
the icon or icon set is not available, true by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.xwiki.icon.macro.internal.DisplayIconMacro
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* 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.icon.macro;

import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.icon.Icon;
import org.xwiki.icon.IconException;
import org.xwiki.icon.IconSet;
import org.xwiki.icon.IconSetCache;
import org.xwiki.icon.IconSetManager;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.observation.ObservationManager;
import org.xwiki.rendering.test.integration.junit5.RenderingTests;
import org.xwiki.script.ScriptContextInitializer;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.ContextualAuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.skin.SkinManager;
import org.xwiki.skinx.SkinExtension;
import org.xwiki.test.annotation.AllComponents;
import org.xwiki.test.mockito.MockitoComponentManager;

import com.xpn.xwiki.doc.XWikiDocument;

import static org.mockito.Mockito.when;

/**
* Run all tests found in {@code *.test} files located in the classpath. These {@code *.test} files must follow the
* conventions described in {@link org.xwiki.rendering.test.integration.TestDataParser}.
*
* @version $Id$
*/
@AllComponents
public class IntegrationTests implements RenderingTests
{
private static final DocumentReference ICON_DOCUMENT_REFERENCE = new DocumentReference("xwiki", "Icon", "Document");

/**
* Initializes various mocks to prevent errors in the integration tests.
*
* @param componentManager the component manager of the tests
* @throws Exception when the initialization fails
*/
@RenderingTests.Initialized
public void initialize(MockitoComponentManager componentManager) throws Exception
{
// Mock the authorization managers as they try initializing a cache which fails (infinispan is not available
// in rendering tests).
ContextualAuthorizationManager authorizationManager =
componentManager.registerMockComponent(ContextualAuthorizationManager.class);
// Grant view right on the icon document.
when(authorizationManager.hasAccess(Right.VIEW, ICON_DOCUMENT_REFERENCE)).thenReturn(true);
componentManager.registerMockComponent(AuthorizationManager.class);

// Mock the icon set cache as it fails.
componentManager.registerMockComponent(IconSetCache.class);

// Mock skin extensions.
componentManager.registerMockComponent(SkinExtension.class, "ssx");
componentManager.registerMockComponent(SkinExtension.class, "jsx");
componentManager.registerMockComponent(SkinExtension.class, "linkx");

// Mock various components for the script context initialization.
componentManager.registerMockComponent(SkinManager.class);
componentManager.registerMockComponent(ScriptContextInitializer.class, "xwiki");
componentManager.registerMockComponent(ObservationManager.class);

DocumentAccessBridge documentAccessBridge = componentManager.registerMockComponent(DocumentAccessBridge.class);
XWikiDocument testDocument = new XWikiDocument(ICON_DOCUMENT_REFERENCE);
when(documentAccessBridge.getDocumentInstance(ICON_DOCUMENT_REFERENCE)).thenReturn(testDocument);

// Mock the icon set manager as we're not in a real enviornment where icon sets can be loaded.
IconSetManager iconSetManager = componentManager.registerMockComponent(IconSetManager.class);
setupIconThemes(iconSetManager);
}

private void setupIconThemes(IconSetManager iconSetManager) throws IconException
{
// The current icon set, the test icon set.
IconSet iconSet = new IconSet("test");
iconSet.addIcon("home", new Icon("homeIcon"));
iconSet.addIcon("page", new Icon("pageIcon"));
iconSet.setRenderWiki("(% class=\"icon\" data-xwiki-icon=\"$icon\" %)i(%%)");
when(iconSetManager.getCurrentIconSet()).thenReturn(iconSet);

// A special icon set to test loading icons from a different icon set.
IconSet specialSet = new IconSet("special");
specialSet.addIcon("home", new Icon("home"));
specialSet.setRenderWiki("special $icon");
when(iconSetManager.getIconSet("special")).thenReturn(specialSet);

// A document-based icon set to test executing in the context of a document.
IconSet documentIconSet = new IconSet("document");
documentIconSet.addIcon("document", new Icon("executed"));
documentIconSet.setRenderWiki("document $icon");
documentIconSet.setSourceDocumentReference(new DocumentReference(ICON_DOCUMENT_REFERENCE));
when(iconSetManager.getIconSet("document")).thenReturn(documentIconSet);

// The default icon set to test fallback to the default when the current or specified icon set doesn't
// contain an icon.
IconSet defaultSet = new IconSet("default");
defaultSet.addIcon("fallback", new Icon("fallbackIcon"));
defaultSet.setRenderWiki("fallback $icon");
when(iconSetManager.getDefaultIconSet()).thenReturn(defaultSet);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* 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.icon.macro.internal;

import java.util.List;
import java.util.concurrent.Callable;

import javax.inject.Named;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.bridge.internal.DocumentContextExecutor;
import org.xwiki.icon.Icon;
import org.xwiki.icon.IconException;
import org.xwiki.icon.IconRenderer;
import org.xwiki.icon.IconSet;
import org.xwiki.icon.IconSetManager;
import org.xwiki.icon.macro.DisplayIconMacroParameters;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.MetaDataBlock;
import org.xwiki.rendering.block.WordBlock;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.rendering.macro.MacroContentParser;
import org.xwiki.rendering.macro.MacroExecutionException;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.rendering.transformation.MacroTransformationContext;
import org.xwiki.security.authorization.ContextualAuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;
import org.xwiki.user.UserReferenceSerializer;

import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.test.MockitoOldcore;
import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

/**
* Unit test for {@link DisplayIconMacro}.
*
* @version $Id$
*/
@OldcoreTest
class DisplayIconMacroTest
{
private static final DocumentReference ICON_DOCUMENT_REFERENCE = new DocumentReference("xwiki", "Icon", "Document");

private static final DocumentReference AUTHOR = new DocumentReference("xwiki", "XWiki", "Author");

@MockComponent
private ContextualAuthorizationManager contextualAuthorizationManager;

@MockComponent
private IconSetManager iconSetManager;

@MockComponent
private IconRenderer iconRenderer;

@MockComponent
private MacroContentParser macroContentParser;

@MockComponent
private DocumentContextExecutor documentContextExecutor;

@MockComponent
private BlockAsyncRendererExecutor blockAsyncRendererExecutor;

@MockComponent
private EntityReferenceSerializer<String> defaultEntityReferenceSerializer;

@MockComponent
@Named("document")
private UserReferenceSerializer<DocumentReference> documentUserSerializer;

@MockComponent
private DocumentAccessBridge documentAccessBridge;

@InjectMockComponents
private DisplayIconMacro displayIconMacro;

private final DisplayIconMacroParameters displayIconMacroParameters = new DisplayIconMacroParameters();

private XWikiDocument iconDocument;

@BeforeEach
public void before(MockitoOldcore oldcore) throws Exception
{
this.iconDocument = new XWikiDocument(ICON_DOCUMENT_REFERENCE);
this.iconDocument.setContentAuthorReference(AUTHOR);
oldcore.getSpyXWiki().saveDocument(this.iconDocument, oldcore.getXWikiContext());

IconSet documentIconSet = new IconSet("document");
documentIconSet.addIcon("home", new Icon("homeIcon"));
documentIconSet.setRenderWiki("icon $icon context {{contextDocumentAuthor /}}");
documentIconSet.setSourceDocumentReference(ICON_DOCUMENT_REFERENCE);
when(this.iconSetManager.getIconSet("document")).thenReturn(documentIconSet);

this.displayIconMacroParameters.setName("home");
this.displayIconMacroParameters.setIconSet("document");

when(this.iconRenderer.render(anyString(), any(IconSet.class)))
.then(invocation -> invocation.getArgument(0, String.class));
when(this.macroContentParser.parse(anyString(), eq(Syntax.XWIKI_2_1), any(), eq(false), any(), anyBoolean()))
.then(invocation -> new XDOM(List.of(new WordBlock(invocation.getArgument(0)))));
when(this.documentContextExecutor.call(any(), any()))
.then(invocation -> invocation.getArgument(0, Callable.class).call());
when(this.blockAsyncRendererExecutor.execute(any())).then(invocation -> invocation.getArgument(0,
BlockAsyncRendererConfiguration.class).getBlock());
when(this.defaultEntityReferenceSerializer.serialize(ICON_DOCUMENT_REFERENCE))
.thenReturn("xwiki:Icon.Document");
when(this.documentUserSerializer.serialize(any())).thenReturn(AUTHOR);
when(this.documentAccessBridge.getDocumentInstance(ICON_DOCUMENT_REFERENCE)).thenReturn(this.iconDocument);
}

@Test
void accessDenied()
{
when(this.contextualAuthorizationManager.hasAccess(Right.VIEW, ICON_DOCUMENT_REFERENCE)).thenReturn(false);
this.displayIconMacroParameters.setFallback(false);

MacroExecutionException executionException = assertThrows(MacroExecutionException.class,
() -> this.displayIconMacro.execute(this.displayIconMacroParameters, null,
mock(MacroTransformationContext.class)));
assertEquals(String.format("Current user [%s] doesn't have view rights on the icon set's document [%s]", null,
ICON_DOCUMENT_REFERENCE), executionException.getMessage());
}

@Test
void fallbackWhenAccessDenied() throws MacroExecutionException, IconException
{
when(this.contextualAuthorizationManager.hasAccess(Right.VIEW, ICON_DOCUMENT_REFERENCE)).thenReturn(false);
IconSet defaultIconSet = mock(IconSet.class);
when(this.iconSetManager.getDefaultIconSet()).thenReturn(defaultIconSet);

List<Block> result =
this.displayIconMacro.execute(this.displayIconMacroParameters, null, new MacroTransformationContext());
assertEquals(result, List.of(new MetaDataBlock(List.of(new WordBlock("home")))));
verify(this.iconRenderer).render("home", defaultIconSet);
verifyNoInteractions(this.documentContextExecutor);
}

@Test
void throwsWhenRenderingIconFails() throws IconException
{
when(this.contextualAuthorizationManager.hasAccess(Right.VIEW, ICON_DOCUMENT_REFERENCE)).thenReturn(true);

IconException testException = new IconException("Test");
when(this.iconRenderer.render("home", this.iconSetManager.getIconSet("document"))).thenThrow(testException);

MacroExecutionException result = assertThrows(MacroExecutionException.class, () ->
this.displayIconMacro.execute(this.displayIconMacroParameters, null, new MacroTransformationContext()));

assertEquals("Failed parsing and executing the icon.", result.getMessage());
assertSame(testException, result.getCause());
}

@Test
void executesInContext() throws Exception
{
when(this.contextualAuthorizationManager.hasAccess(Right.VIEW, ICON_DOCUMENT_REFERENCE)).thenReturn(true);

List<Block> result =
this.displayIconMacro.execute(this.displayIconMacroParameters, null, new MacroTransformationContext());
assertEquals(result, List.of(new MetaDataBlock(List.of(new WordBlock("home")))));
verify(this.documentContextExecutor).call(any(), eq(this.iconDocument));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.runTransformations
.#-----------------------------------------------------
.input|xwiki/2.1
.# Verify the icon macro basic functionality
.#-----------------------------------------------------
Home icon: {{displayIcon name="home" /}}

{{displayIcon name="page" /}}
.#-----------------------------------------------------
.expect|event/1.0
.#-----------------------------------------------------
beginDocument
beginParagraph
onWord [Home]
onSpace
onWord [icon]
onSpecialSymbol [:]
onSpace
beginMacroMarkerInline [displayIcon] [name=home]
beginMetaData [[syntax]=[XWiki 2.1]]
beginFormat [NONE] [[class]=[icon][data-xwiki-icon]=[homeIcon]]
onWord [i]
endFormat [NONE] [[class]=[icon][data-xwiki-icon]=[homeIcon]]
endMetaData [[syntax]=[XWiki 2.1]]
endMacroMarkerInline [displayIcon] [name=home]
endParagraph
beginMacroMarkerStandalone [displayIcon] [name=page]
beginMetaData [[syntax]=[XWiki 2.1]]
beginParagraph
beginFormat [NONE] [[class]=[icon][data-xwiki-icon]=[pageIcon]]
onWord [i]
endFormat [NONE] [[class]=[icon][data-xwiki-icon]=[pageIcon]]
endParagraph
endMetaData [[syntax]=[XWiki 2.1]]
endMacroMarkerStandalone [displayIcon] [name=page]
endDocument
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.runTransformations
.#-----------------------------------------------------
.input|xwiki/2.1
.# Verify that the icon set can be specified.
.#-----------------------------------------------------
{{displayIcon name="home" iconSet="special" /}}
.#-----------------------------------------------------
.expect|event/1.0
.#-----------------------------------------------------
beginDocument
beginMacroMarkerStandalone [displayIcon] [name=home|iconSet=special]
beginMetaData [[syntax]=[XWiki 2.1]]
beginParagraph
onWord [special]
onSpace
onWord [home]
endParagraph
endMetaData [[syntax]=[XWiki 2.1]]
endMacroMarkerStandalone [displayIcon] [name=home|iconSet=special]
endDocument
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.runTransformations
.#-----------------------------------------------------
.input|xwiki/2.1
.# Verify that the fallback to the default works and can be disabled.
.#-----------------------------------------------------
{{displayIcon name="fallback" iconSet="special" /}} {{displayIcon name="fallback" /}} {{displayIcon name="fallback" fallback="false" /}}

{{displayIcon name="home" iconSet="none" fallback="false" /}}
.#-----------------------------------------------------
.expect|event/1.0
.#-----------------------------------------------------
beginDocument
beginParagraph
beginMacroMarkerInline [displayIcon] [name=fallback|iconSet=special]
beginMetaData [[syntax]=[XWiki 2.1]]
onWord [fallback]
onSpace
onWord [fallbackIcon]
endMetaData [[syntax]=[XWiki 2.1]]
endMacroMarkerInline [displayIcon] [name=fallback|iconSet=special]
onSpace
beginMacroMarkerInline [displayIcon] [name=fallback]
beginMetaData [[syntax]=[XWiki 2.1]]
onWord [fallback]
onSpace
onWord [fallbackIcon]
endMetaData [[syntax]=[XWiki 2.1]]
endMacroMarkerInline [displayIcon] [name=fallback]
onSpace
beginMacroMarkerInline [displayIcon] [name=fallback|fallback=false]
beginMetaData
endMetaData
endMacroMarkerInline [displayIcon] [name=fallback|fallback=false]
endParagraph
beginMacroMarkerStandalone [displayIcon] [name=home|iconSet=none|fallback=false]
endMacroMarkerStandalone [displayIcon] [name=home|iconSet=none|fallback=false]
endDocument
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.runTransformations
.#-----------------------------------------------------
.input|xwiki/2.1
.# Verify the document-based icon set is rendered in document context
.#-----------------------------------------------------
{{displayIcon name="document" iconSet="document" /}}
.#-----------------------------------------------------
.expect|event/1.0
.#-----------------------------------------------------
beginDocument
beginMacroMarkerStandalone [displayIcon] [name=document|iconSet=document]
beginMetaData [[source]=[xwiki:Icon.Document][syntax]=[XWiki 2.1]]
beginParagraph
onWord [document]
onSpace
onWord [executed]
endParagraph
endMetaData [[source]=[xwiki:Icon.Document][syntax]=[XWiki 2.1]]
endMacroMarkerStandalone [displayIcon] [name=document|iconSet=document]
endDocument