diff --git a/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentAccessBridge.java b/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentAccessBridge.java index fa492c7d2908..80a0a8db01c3 100644 --- a/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentAccessBridge.java +++ b/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentAccessBridge.java @@ -314,9 +314,17 @@ void setDocumentContent(String documentName, String content, String editComment, /** * @param documentName The name of the document to be edited. * @return True if current user has 'edit' access on the target document. + * @deprecated use {@link #isDocumentEditable(DocumentName)} instead */ boolean isDocumentEditable(String documentName); + /** + * @param documentName the name of the document to be edited. + * @return True if current user has 'edit' access on the target document. + * @since 2.2M1 + */ + boolean isDocumentEditable(DocumentName documentName); + /** * @return true if the current document's author has programming rights. */ diff --git a/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java b/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java index b9a8df8e1364..a66d8ef97ed9 100644 --- a/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java +++ b/xwiki-bridge/src/main/java/org/xwiki/bridge/DocumentModelBridge.java @@ -47,9 +47,15 @@ public interface DocumentModelBridge * Retrieve the full name of the document, in the Space.Name format, for example Main.WebHome. * * @return A String representation of the document's full name. + * @deprecated use #getDocumentName instead */ String getFullName(); + /** + * @return the full document's name, including Wiki, Space and Page + */ + DocumentName getDocumentName(); + /** * Retrieve the name of the virtual wiki this document belongs to. * diff --git a/xwiki-core/pom.xml b/xwiki-core/pom.xml index 06b006de273a..d31226bf6aa3 100644 --- a/xwiki-core/pom.xml +++ b/xwiki-core/pom.xml @@ -550,11 +550,6 @@ - - org.xwiki.platform - xwiki-core-component-default - ${pom.version} - org.xwiki.platform xwiki-core-configuration-default @@ -615,7 +610,8 @@ xwiki-core-chart-renderer ${pom.version} - + org.xwiki.platform @@ -793,6 +789,13 @@ --> runtime + + org.xwiki.platform + xwiki-core-component-multi + ${pom.version} + + runtime + org.xwiki.platform xwiki-core-query-xwql diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/doc/DefaultDocumentAccessBridge.java b/xwiki-core/src/main/java/com/xpn/xwiki/doc/DefaultDocumentAccessBridge.java index c372544a3871..421a123177b7 100644 --- a/xwiki-core/src/main/java/com/xpn/xwiki/doc/DefaultDocumentAccessBridge.java +++ b/xwiki-core/src/main/java/com/xpn/xwiki/doc/DefaultDocumentAccessBridge.java @@ -495,12 +495,24 @@ public boolean isDocumentViewable(String documentName) * {@inheritDoc} * * @see DocumentAccessBridge#isDocumentEditable(String) + * @deprecated use {@link #isDocumentEditable(DocumentName)} instead */ public boolean isDocumentEditable(String documentName) { return hasRight(documentName, "edit"); } + /** + * {@inheritDoc} + * + * @see DocumentAccessBridge#isDocumentEditable(org.xwiki.bridge.DocumentName) + * @since 2.2M1 + */ + public boolean isDocumentEditable(DocumentName documentName) + { + return hasRight(this.documentNameSerializer.serialize(documentName), "edit"); + } + /** * {@inheritDoc} * diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroFactory.java b/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroFactory.java index d6a9daa862ac..710e0f454756 100644 --- a/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroFactory.java +++ b/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroFactory.java @@ -25,6 +25,7 @@ import java.util.Vector; import org.apache.commons.lang.StringUtils; +import org.xwiki.bridge.DocumentName; import org.xwiki.bridge.DocumentNameSerializer; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.Requirement; @@ -40,6 +41,8 @@ import org.xwiki.rendering.macro.wikibridge.WikiMacroException; import org.xwiki.rendering.macro.wikibridge.WikiMacroDescriptor; import org.xwiki.rendering.macro.wikibridge.WikiMacroParameterDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroVisibility; + import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; @@ -70,7 +73,7 @@ public class DefaultWikiMacroFactory extends AbstractLogEnabled implements WikiM * Used to serialize document names into strings. */ @Requirement - private DocumentNameSerializer docNameSerializer; + private DocumentNameSerializer documentNameSerializer; /** * Utility method for accessing XWikiContext. @@ -85,11 +88,11 @@ private XWikiContext getContext() /** * {@inheritDoc} */ - public WikiMacro createWikiMacro(String documentName) throws WikiMacroException + public WikiMacro createWikiMacro(DocumentName documentName) throws WikiMacroException { - XWikiDocument doc = null; + XWikiDocument doc; try { - doc = getContext().getWiki().getDocument(documentName, getContext()); + doc = getContext().getWiki().getDocument(this.documentNameSerializer.serialize(documentName), getContext()); } catch (XWikiException ex) { throw new WikiMacroException(String.format( "Could not build macro from : [%s], unable to load document", documentName), ex); @@ -100,19 +103,18 @@ public WikiMacro createWikiMacro(String documentName) throws WikiMacroException /** * Creates a {@link WikiMacro} from an {@link XWikiDocument} which contains a macro definition. * - * @param doc the {@link XWikiDocument} to look for a macro definition. - * @return a {@link WikiMacro} found inside the document. - * @throws org.xwiki.rendering.macro.wikibridge.WikiMacroException invalid macro definition / no macro definition found. + * @param doc the {@link XWikiDocument} to look for a macro definition + * @return the {@link WikiMacro} found inside the document + * @throws WikiMacroException when an invalid macro definition or no macro definition was found */ private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException { - String fullDocumentName = docNameSerializer.serialize(doc.getDocumentName()); + DocumentName documentName = doc.getDocumentName(); // Check whether this document contains a macro definition. BaseObject macroDefinition = doc.getObject(WIKI_MACRO_CLASS); if (null == macroDefinition) { - throw new WikiMacroException(String.format("No macro definition found in document : [%s]", - fullDocumentName)); + throw new WikiMacroException(String.format("No macro definition found in document : [%s]", documentName)); } // Extract macro definition. @@ -120,6 +122,8 @@ private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException String macroName = macroDefinition.getStringValue(MACRO_NAME_PROPERTY); String macroDescription = macroDefinition.getStringValue(MACRO_DESCRIPTION_PROPERTY); String macroDefaultCategory = macroDefinition.getStringValue(MACRO_DEFAULT_CATEGORY_PROPERTY); + WikiMacroVisibility macroVisibility = WikiMacroVisibility.fromString(macroDefinition.getStringValue( + MACRO_VISIBILITY_PROPERTY)); boolean macroSupportsInlineMode = (macroDefinition.getIntValue(MACRO_INLINE_PROPERTY) == 0) ? false : true; String macroContentType = macroDefinition.getStringValue(MACRO_CONTENT_TYPE_PROPERTY); String macroContentDescription = macroDefinition.getStringValue(MACRO_CONTENT_DESCRIPTION_PROPERTY); @@ -128,29 +132,27 @@ private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException // Verify macro id. if (StringUtils.isEmpty(macroId)) { throw new WikiMacroException(String.format( - "Incomplete macro definition in [%s], macro id is empty", fullDocumentName)); + "Incomplete macro definition in [%s], macro id is empty", documentName)); } // Verify macro name. if (StringUtils.isEmpty(macroName)) { macroName = macroId; getLogger().warn( - String.format("Incomplete macro definition in [%s], macro name is empty", fullDocumentName)); + String.format("Incomplete macro definition in [%s], macro name is empty", documentName)); } // Verify macro description. if (StringUtils.isEmpty(macroDescription)) { getLogger().warn( - String.format("Incomplete macro definition in [%s], macro description is empty", fullDocumentName)); + String.format("Incomplete macro definition in [%s], macro description is empty", documentName)); } // Verify default macro category. if (StringUtils.isEmpty(macroDefaultCategory)) { macroDefaultCategory = null; - getLogger() - .warn( - String.format("Incomplete macro definition in [%s], default macro category is empty", - fullDocumentName)); + getLogger().warn(String.format("Incomplete macro definition in [%s], default macro category is empty", + documentName)); } // Verify macro content type. @@ -161,14 +163,14 @@ private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException // Verify macro content description. if (!macroContentType.equals(MACRO_CONTENT_EMPTY) && StringUtils.isEmpty(macroContentDescription)) { String errorMsg = "Incomplete macro definition in [%s], macro content description is empty"; - getLogger().warn(String.format(errorMsg, fullDocumentName)); + getLogger().warn(String.format(errorMsg, documentName)); macroContentDescription = "Macro content"; } // Verify macro code. if (StringUtils.isEmpty(macroCode)) { throw new WikiMacroException(String.format( - "Incomplete macro definition in [%s], macro code is empty", fullDocumentName)); + "Incomplete macro definition in [%s], macro code is empty", documentName)); } // Extract macro parameters. @@ -190,13 +192,13 @@ private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException // Verify parameter name. if (StringUtils.isEmpty(parameterName)) { throw new WikiMacroException(String.format( - "Incomplete macro definition in [%s], macro parameter name is empty", fullDocumentName)); + "Incomplete macro definition in [%s], macro parameter name is empty", documentName)); } // Verify parameter description. if (StringUtils.isEmpty(parameterDescription)) { String errorMessage = "Incomplete macro definition in [%s], macro parameter description is empty"; - getLogger().warn(String.format(errorMessage, fullDocumentName)); + getLogger().warn(String.format(errorMessage, documentName)); } // Create the parameter descriptor. @@ -214,21 +216,22 @@ private WikiMacro buildMacro(XWikiDocument doc) throws WikiMacroException // Create macro descriptor. MacroDescriptor macroDescriptor = new WikiMacroDescriptor(macroName, macroDescription, macroDefaultCategory, - contentDescriptor, parameterDescriptors); + macroVisibility, contentDescriptor, parameterDescriptors); // Create & return the macro. - return new DefaultWikiMacro(fullDocumentName, macroId, macroSupportsInlineMode, macroDescriptor, macroCode, + return new DefaultWikiMacro(documentName, macroId, macroSupportsInlineMode, macroDescriptor, macroCode, doc.getSyntaxId(), componentManager); } /** * {@inheritDoc} */ - public boolean containsWikiMacro(String documentName) + public boolean containsWikiMacro(DocumentName documentName) { - boolean result = true; + boolean result; try { - XWikiDocument doc = getContext().getWiki().getDocument(documentName, getContext()); + XWikiDocument doc = getContext().getWiki().getDocument( + this.documentNameSerializer.serialize(documentName), getContext()); BaseObject macroDefinition = doc.getObject(WIKI_MACRO_CLASS); result = (null != macroDefinition); } catch (XWikiException ex) { diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroInitializer.java b/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroInitializer.java index bcc588b17036..8fb5ad35ddd2 100644 --- a/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroInitializer.java +++ b/xwiki-core/src/main/java/com/xpn/xwiki/internal/DefaultWikiMacroInitializer.java @@ -20,11 +20,12 @@ package com.xpn.xwiki.internal; import java.util.Arrays; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; import org.apache.commons.lang.StringUtils; +import org.xwiki.bridge.DocumentName; +import org.xwiki.bridge.DocumentNameFactory; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.Requirement; import org.xwiki.component.logging.AbstractLogEnabled; @@ -50,11 +51,6 @@ @Component public class DefaultWikiMacroInitializer extends AbstractLogEnabled implements WikiMacroInitializer, WikiMacroConstants { - /** - * Main wiki identifier. - */ - private static final String MAIN_WIKI = "xwiki"; - /** * The {@link org.xwiki.rendering.macro.wikibridge.WikiMacroFactory} component. */ @@ -73,6 +69,9 @@ public class DefaultWikiMacroInitializer extends AbstractLogEnabled implements W @Requirement private Execution execution; + @Requirement + private DocumentNameFactory documentNameFactory; + /** * Utility method for accessing XWikiContext. * @@ -98,41 +97,69 @@ public void registerExistingWikiMacros() throws Exception throw new Exception(String.format(message, WIKI_MACRO_CLASS, WIKI_MACRO_PARAMETER_CLASS)); } - // Only consider the main wiki. - xcontext.setDatabase(MAIN_WIKI); + // Register the wiki macros that exist in each wiki + String originalWiki = xcontext.getDatabase(); + try { + // If we're in multi wiki mode get the list of all subwikis, otherwise just look into the main wiki + List wikiNames; + if (xcontext.getWiki().isVirtualMode()) { + wikiNames = xcontext.getWiki().getVirtualWikisDatabaseNames(xcontext); + } else { + wikiNames = Collections.singletonList(xcontext.getMainXWiki()); + } + + for (String wikiName : wikiNames) { + // Set the context to be in that wiki so that both the search for XWikiMacro class objects and the + // registration of macros refistered for the current wiki will work. + // TODO: In the future when we have APIs for it, move the code to set the current wiki and the current + // user (see below) to the WikiMacroManager's implementation. + xcontext.setDatabase(wikiName); + + // Search for all those documents with macro definitions and for each register the macro + for (Object[] wikiMacroDocumentData : getWikiMacroDocumentData(xcontext)) { + DocumentName wikiMacroDocumentName = this.documentNameFactory.createDocumentName( + wikiName + ":" + wikiMacroDocumentData[0]); + String wikiMacroDocumentAuthor = (String) wikiMacroDocumentData[1]; + try { + WikiMacro macro = wikiMacroFactory.createWikiMacro(wikiMacroDocumentName); + + // Set the author in the context to be the author who last modified the document containing + // the wiki macro class definition, so that if the Macro has the "Current User" visibility + // the correct user will be found in the Execution Context. + String originalAuthor = xcontext.getUser(); + try { + xcontext.setUser(wikiMacroDocumentAuthor); + wikiMacroManager.registerWikiMacro(wikiMacroDocumentName, macro); + } finally { + xcontext.setUser(originalAuthor); + } + } catch (WikiMacroException ex) { + // Just log the exception and skip to the next. + getLogger().error(ex.getMessage(), ex); + } + } + } + } finally { + xcontext.setDatabase(originalWiki); + } + } - // Search for all those documents with macro definitions. + private List getWikiMacroDocumentData(XWikiContext xcontext) throws Exception + { // TODO: Use the query manager instead - String sql = - "select doc.fullName from XWikiDocument doc, BaseObject obj where doc.fullName=obj.name and obj.className=?"; - List wikiMacroDocs = null; + String sql = "select doc.fullName, doc.author from XWikiDocument doc, BaseObject obj where " + + "doc.fullName=obj.name and obj.className=?"; + List wikiMacroDocumentData; try { - wikiMacroDocs = xcontext.getWiki().getStore().search(sql, 0, 0, Arrays.asList(WIKI_MACRO_CLASS), xcontext); + wikiMacroDocumentData = xcontext.getWiki().getStore().search(sql, 0, 0, Arrays.asList(WIKI_MACRO_CLASS), + xcontext); } catch (XWikiException ex) { throw new Exception("Error while searching for macro documents", ex); } - // Build macros. - Map wikiMacros = new HashMap(); - for (Object obj : wikiMacroDocs) { - String wikiMacroDoc = (String) obj; - try { - WikiMacro macro = wikiMacroFactory.createWikiMacro(wikiMacroDoc); - wikiMacros.put(wikiMacroDoc, macro); - } catch (WikiMacroException ex) { - // Just log the exception and skip to the next. - getLogger().error(ex.getMessage(), ex); - } - } - - // Register the wiki macros against WikiMacroManager. - for (String documentName : wikiMacros.keySet()) { - // TODO: Fix this to allow macros to be registered for any wiki - // TODO: In addition the main wiki name should never be hardcoded!! - wikiMacroManager.registerWikiMacro(MAIN_WIKI + ":" + documentName, wikiMacros.get(documentName)); - } + return wikiMacroDocumentData; } - + private boolean setWikiMacroClassesDocumentFields(XWikiDocument doc, String title) { boolean needsUpdate = false; @@ -182,12 +209,12 @@ public void installOrUpgradeWikiMacroClasses() throws Exception needsUpdate |= bclass.addTextAreaField(MACRO_DESCRIPTION_PROPERTY, "Macro description", 40, 5); needsUpdate |= bclass.addTextField(MACRO_DEFAULT_CATEGORY_PROPERTY, "Default category", 30); needsUpdate |= bclass.addBooleanField(MACRO_INLINE_PROPERTY, "Supports inline mode", "yesno"); - needsUpdate |= - bclass.addStaticListField(MACRO_CONTENT_TYPE_PROPERTY, "Macro content type", 1, false, - "Mandatory|Optional|No content", "select", "|"); - needsUpdate |= - bclass.addTextAreaField(MACRO_CONTENT_DESCRIPTION_PROPERTY, - "Content description (Not applicable for \"No content\" type)", 40, 5); + needsUpdate |= bclass.addStaticListField(MACRO_VISIBILITY_PROPERTY, "Macro visibility", 1, false, + "Current User|Current Wiki|Global", "select", "|"); + needsUpdate |= bclass.addStaticListField(MACRO_CONTENT_TYPE_PROPERTY, "Macro content type", 1, false, + "Mandatory|Optional|No content", "select", "|"); + needsUpdate |= bclass.addTextAreaField(MACRO_CONTENT_DESCRIPTION_PROPERTY, + "Content description (Not applicable for \"No content\" type)", 40, 5); needsUpdate |= bclass.addTextAreaField(MACRO_CODE_PROPERTY, "Macro code", 40, 20); if (needsUpdate) { diff --git a/xwiki-core/src/main/java/com/xpn/xwiki/internal/WikiMacroConstants.java b/xwiki-core/src/main/java/com/xpn/xwiki/internal/WikiMacroConstants.java index 6fe1b53739fa..f9f4e78d574a 100644 --- a/xwiki-core/src/main/java/com/xpn/xwiki/internal/WikiMacroConstants.java +++ b/xwiki-core/src/main/java/com/xpn/xwiki/internal/WikiMacroConstants.java @@ -17,7 +17,6 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ - package com.xpn.xwiki.internal; /** @@ -53,6 +52,11 @@ public interface WikiMacroConstants */ String MACRO_DEFAULT_CATEGORY_PROPERTY = "defaultCategory"; + /** + * Name of the macro visibility property in the Wiki Macro Class. + */ + String MACRO_VISIBILITY_PROPERTY = "visibility"; + /** * Constant for representing macro inline support property. */ diff --git a/xwiki-core/src/test/java/com/xpn/xwiki/internal/DefaultWikiMacroBuilderTest.java b/xwiki-core/src/test/java/com/xpn/xwiki/internal/DefaultWikiMacroFactoryTest.java similarity index 85% rename from xwiki-core/src/test/java/com/xpn/xwiki/internal/DefaultWikiMacroBuilderTest.java rename to xwiki-core/src/test/java/com/xpn/xwiki/internal/DefaultWikiMacroFactoryTest.java index 65d10b6dd401..30ce61919f84 100644 --- a/xwiki-core/src/test/java/com/xpn/xwiki/internal/DefaultWikiMacroBuilderTest.java +++ b/xwiki-core/src/test/java/com/xpn/xwiki/internal/DefaultWikiMacroFactoryTest.java @@ -17,16 +17,18 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ - package com.xpn.xwiki.internal; import org.jmock.Mock; +import org.xwiki.bridge.DocumentName; import org.xwiki.rendering.macro.wikibridge.WikiMacro; +import org.xwiki.rendering.macro.wikibridge.WikiMacroDescriptor; import org.xwiki.rendering.macro.wikibridge.WikiMacroFactory; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.test.AbstractBridgedXWikiComponentTestCase; +import org.xwiki.rendering.macro.wikibridge.WikiMacroVisibility; /** * Unit test for {@link DefaultWikiMacroFactory}. @@ -34,7 +36,7 @@ * @since 2.0M3 * @version $Id$ */ -public class DefaultWikiMacroBuilderTest extends AbstractBridgedXWikiComponentTestCase +public class DefaultWikiMacroFactoryTest extends AbstractBridgedXWikiComponentTestCase { private XWikiDocument macroDefinitionDoc; @@ -55,6 +57,7 @@ protected void setUp() throws Exception obj.setStringValue("name", "Test Macro"); obj.setStringValue("description", "This is a macro used for testing purposes."); obj.setStringValue("defaultCategory", "Test"); + obj.setStringValue("visibility", "Current User"); obj.setIntValue("supportsInlineMode", 1); obj.setStringValue("contentType", "No content"); obj.setStringValue("code", "==Hi=="); @@ -69,10 +72,10 @@ protected void setUp() throws Exception getContext().setWiki((XWiki) mockXWiki.proxy()); } - public void testWikiMacroBuilding() throws Exception + public void testCreateWikiMacro() throws Exception { // Build a wiki macro. - WikiMacro macro = this.wikiMacroFactory.createWikiMacro("xwiki:Macros.Test"); + WikiMacro macro = this.wikiMacroFactory.createWikiMacro(new DocumentName("xwiki", "Macros", "Test")); assertNotNull(macro); // Check if the macro was built correctly. @@ -80,25 +83,21 @@ public void testWikiMacroBuilding() throws Exception assertEquals("Test Macro", macro.getDescriptor().getName()); assertEquals("This is a macro used for testing purposes.", macro.getDescriptor().getDescription()); assertEquals("Test", macro.getDescriptor().getDefaultCategory()); + assertEquals(WikiMacroVisibility.USER, ((WikiMacroDescriptor) macro.getDescriptor()).getVisibility()); assertTrue(macro.supportsInlineMode()); assertNull(macro.getDescriptor().getContentDescriptor()); } - public void testWikiMacroWithoutName() throws Exception + public void testCreateWikiMacroWithoutName() throws Exception { BaseObject obj = macroDefinitionDoc.getObject("XWiki.WikiMacroClass"); obj.setStringValue("name", ""); // Build a wiki macro. - WikiMacro macro = this.wikiMacroFactory.createWikiMacro("xwiki:Macros.Test"); + WikiMacro macro = this.wikiMacroFactory.createWikiMacro(new DocumentName("xwiki", "Macros", "Test")); assertNotNull(macro); // Check if the macro was built correctly. - assertEquals("testmacro", macro.getId()); assertEquals("testmacro", macro.getDescriptor().getName()); - assertEquals("This is a macro used for testing purposes.", macro.getDescriptor().getDescription()); - assertEquals("Test", macro.getDescriptor().getDefaultCategory()); - assertTrue(macro.supportsInlineMode()); - assertNull(macro.getDescriptor().getContentDescriptor()); } } diff --git a/xwiki-rendering/xwiki-rendering-api/pom.xml b/xwiki-rendering/xwiki-rendering-api/pom.xml index b8e98864eeb0..13e99906c257 100644 --- a/xwiki-rendering/xwiki-rendering-api/pom.xml +++ b/xwiki-rendering/xwiki-rendering-api/pom.xml @@ -128,6 +128,7 @@ **/rendering/configuration/*.java, **/rendering/converter/*.java, **/rendering/internal/configuration/*.java, + **/rendering/internal/macro/*.java, **/rendering/internal/parser/*.java, **/rendering/internal/renderer/BasicLinkRenderer.java, **/rendering/listener/*.java, diff --git a/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroCategoryManager.java b/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroCategoryManager.java index a43efc416d97..860c47c5aea9 100644 --- a/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroCategoryManager.java +++ b/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroCategoryManager.java @@ -57,8 +57,15 @@ public class DefaultMacroCategoryManager extends AbstractLogEnabled implements M @Requirement private MacroManager macroManager; + /** + * Internal help class to be able to search Macros matching a Macro Id. + */ private interface MacroMatcher { + /** + * @param macroId the macro Id to match + * @return true if the concerned macro matches the macro Id + */ boolean match(MacroId macroId); } @@ -119,6 +126,7 @@ public boolean match(MacroId macroId) /** * @param matcher a macro name matcher to be able to filter macros, used to filter macros for a given syntax * @return macro names grouped by category, including the 'null' macro category. + * @exception MacroLookupException if any error occurs when getting macros ids by category */ private Map> getMacroIdsByCategory(MacroMatcher matcher) throws MacroLookupException { diff --git a/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroIdFactory.java b/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroIdFactory.java index 5ea8777e76eb..eb319b7567c6 100644 --- a/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroIdFactory.java +++ b/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroIdFactory.java @@ -35,6 +35,15 @@ @Component public class DefaultMacroIdFactory implements MacroIdFactory { + /** + * Error message when the macro id format is invalid. + */ + private static final String INVALID_MACRO_ID_FORMAT = "Invalid macro id format [%s]"; + + /** + * For creating Syntax objects when creating MacroId from a string representing the syntax id and the + * syntax version. + */ @Requirement private SyntaxFactory syntaxFactory; @@ -53,9 +62,10 @@ public MacroId createMacroId(String macroIdAsString) throws ParseException // We've found a macro id for a macro that should be available only for a given syntax Syntax syntax; try { - syntax = this.syntaxFactory.createSyntaxFromIdString(hintParts[1] + "/" + hintParts[2]); + syntax = this.syntaxFactory.createSyntaxFromIdString( + String.format("%s/%s", hintParts[1], hintParts[2])); } catch (ParseException e) { - throw new ParseException("Invalid macro id format [" + macroIdAsString + "]", e); + throw new ParseException(String.format(INVALID_MACRO_ID_FORMAT, macroIdAsString), e); } macroId = new MacroId(hintParts[0], syntax); } else if (hintParts.length == 1) { @@ -63,7 +73,7 @@ public MacroId createMacroId(String macroIdAsString) throws ParseException macroId = new MacroId(macroIdAsString); } else { // Invalid format - throw new ParseException("Invalid macro id format [" + macroIdAsString + "]"); + throw new ParseException(String.format(INVALID_MACRO_ID_FORMAT, macroIdAsString)); } return macroId; diff --git a/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroManager.java b/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroManager.java index b16b63f7bafc..82adc582cab7 100644 --- a/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroManager.java +++ b/xwiki-rendering/xwiki-rendering-api/src/main/java/org/xwiki/rendering/internal/macro/DefaultMacroManager.java @@ -53,10 +53,12 @@ public class DefaultMacroManager extends AbstractLogEnabled implements MacroMana private MacroIdFactory macroIdFactory; /** - * The component manager we use to lookup macro implementations registered as components. + * The Root Component Manager we use find the Context Component Manager (if it exists) to lookup macro + * implementations registered as components. Note that Context Component Manager allows Macros to be + * registered for a specific user, for a specific wiki, etc. */ @Requirement - private ComponentManager componentManager; + private ComponentManager rootComponentManager; /** * {@inheritDoc} @@ -80,7 +82,7 @@ public Set getMacroIds(Syntax syntax) throws MacroLookupException // Lookup all registered macros Map allMacros; try { - allMacros = this.componentManager.lookupMap(Macro.class); + allMacros = getComponentManager().lookupMap(Macro.class); } catch (ComponentLookupException e) { throw new MacroLookupException("Failed to lookup Macros", e); } @@ -118,11 +120,11 @@ public Set getMacroIds(Syntax syntax) throws MacroLookupException // First search for a macro registered for the passed macro id. String macroHint = macroId.toString(); try { - return this.componentManager.lookup(Macro.class, macroHint); + return getComponentManager().lookup(Macro.class, macroHint); } catch (ComponentLookupException ex1) { // Now search explicitly for a macro registered for all syntaxes. try { - return this.componentManager.lookup(Macro.class, macroId.getId()); + return getComponentManager().lookup(Macro.class, macroId.getId()); } catch (ComponentLookupException ex2) { // TODO: Improve this since it's possible the macro wasn't found because it contains some invalid // requirement and since we're not passing the raised exception it's hard to know why the macro @@ -142,10 +144,32 @@ public boolean exists(MacroId macroId) String macroHint = macroId.toString(); boolean hasMacro = true; try { - this.componentManager.lookup(Macro.class, macroHint); + getComponentManager().lookup(Macro.class, macroHint); } catch (ComponentLookupException ex) { hasMacro = false; } return hasMacro; } + + /** + * @return the Component Manager to use to lookup Macros. If the Context Component Manager is available + * we use it thus allowing Macros to be registered only for a given Wiki or for a given User (for example). + * @since 2.2M1 + */ + private ComponentManager getComponentManager() + { + ComponentManager componentManagerToUse; + + // Look for the Context Component Manager so that Macros can be registered for a specific user, for a + // specific wiki, etc. If it's not found use the Root Component Manager. This allows the Rendering module + // to work outside of XWiki when there's no notion of Execution Context and Wiki Model for example. + try { + componentManagerToUse = this.rootComponentManager.lookup(ComponentManager.class, "context"); + } catch (ComponentLookupException e) { + // This means the Context CM doesn't exist, use the Root CM. + componentManagerToUse = this.rootComponentManager; + } + + return componentManagerToUse; + } } diff --git a/xwiki-rendering/xwiki-rendering-api/src/test/java/org/xwiki/rendering/internal/macro/DefaultMacroManagerTest.java b/xwiki-rendering/xwiki-rendering-api/src/test/java/org/xwiki/rendering/internal/macro/DefaultMacroManagerTest.java index e76576429dad..994a1f2aa531 100644 --- a/xwiki-rendering/xwiki-rendering-api/src/test/java/org/xwiki/rendering/internal/macro/DefaultMacroManagerTest.java +++ b/xwiki-rendering/xwiki-rendering-api/src/test/java/org/xwiki/rendering/internal/macro/DefaultMacroManagerTest.java @@ -24,6 +24,7 @@ import org.jmock.Mock; import org.xwiki.component.descriptor.DefaultComponentDescriptor; import org.xwiki.component.logging.Logger; +import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; import org.xwiki.component.util.ReflectionUtils; import org.xwiki.rendering.internal.transformation.TestSimpleMacro; @@ -55,7 +56,7 @@ public class DefaultMacroManagerTest extends AbstractXWikiComponentTestCase /** * {@inheritDoc} * - * @see org.xwiki.rendering.scaffolding.AbstractXWikiComponentTestCase#setUp() + * @see AbstractXWikiComponentTestCase#setUp() */ @Override protected void setUp() throws Exception @@ -145,9 +146,11 @@ public void testMacroRegisteredForAGivenSyntaxOverridesMacroRegisteredForAllSynt */ public void testInvalidMacroHint() throws Exception { + this.mockComponentManager.expects(once()).method("lookup").with( + eq(ComponentManager.class), eq("context")).will(throwException(new ComponentLookupException("error"))); this.mockComponentManager.expects(once()).method("lookupMap").will( returnValue(Collections.singletonMap("macro/invalidsyntax", "dummy"))); - ReflectionUtils.setFieldValue(this.macroManager, "componentManager", this.mockComponentManager.proxy()); + ReflectionUtils.setFieldValue(this.macroManager, "rootComponentManager", this.mockComponentManager.proxy()); // Verify that when a Macro has an invalid hint it's logged as a warning. this.mockLogger.expects(once()).method("warn").with( diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/pom.xml b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/pom.xml index ed688abeaba7..9cd8bf9a0e93 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/pom.xml +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/pom.xml @@ -50,6 +50,13 @@ xwiki-core-context ${pom.version} + + + org.xwiki.platform + xwiki-core-component-multi + ${pom.version} + test + @@ -62,7 +69,18 @@ **/**Test.java - + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + **/rendering/internal/macro/wikibridge/DefaultWikiMacroManager.java, + **/rendering/macro/wikibridge/*.java + + + \ No newline at end of file diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacro.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacro.java index b284e7fb6236..d767f053210a 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacro.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacro.java @@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils; import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.bridge.DocumentName; import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; import org.xwiki.context.Execution; @@ -56,7 +57,7 @@ public class DefaultWikiMacro implements WikiMacro { /** - * The key under which macro context will be available in the XwikiContext for scripts. + * The key under which macro context will be available in the XWikiContext for scripts. */ private static final String MACRO_KEY = "macro"; @@ -93,7 +94,7 @@ public class DefaultWikiMacro implements WikiMacro /** * Document which contains the definition of this macro. */ - private String macroDocument; + private DocumentName macroDocumentName; /** * Id under which this macro is registered with component manager. @@ -128,17 +129,18 @@ public class DefaultWikiMacro implements WikiMacro /** * Constructs a new {@link DefaultWikiMacro}. * - * @param macroDocument document which contains the definition of this macro. + * @param macroDocumentName the name of the document which contains the definition of this macro * @param macroId id under which this macro is registered with component manager. * @param descriptor the {@link MacroDescriptor} describing this macro. * @param macroContent macro content to be evaluated. * @param syntaxId syntax of the macroContent. * @param componentManager {@link ComponentManager} component used to look up for other components. + * @since 2.2M1 */ - public DefaultWikiMacro(String macroDocument, String macroId, boolean supportsInlineMode, + public DefaultWikiMacro(DocumentName macroDocumentName, String macroId, boolean supportsInlineMode, MacroDescriptor descriptor, String macroContent, String syntaxId, ComponentManager componentManager) { - this.macroDocument = macroDocument; + this.macroDocumentName = macroDocumentName; this.macroId = macroId; this.supportsInlineMode = supportsInlineMode; this.descriptor = descriptor; @@ -158,6 +160,8 @@ public List execute(WikiMacroParameters parameters, String macroContent, throws MacroExecutionException { // First verify that all mandatory parameters are provided. + // Note that we currently verify automatically mandatory parameters in Macro Transformation but for the moment + // this is only checked for Java-based macros. Hence why we need to check here too. Map parameterDescriptors = getDescriptor().getParameterDescriptorMap(); for (String parameterName : parameterDescriptors.keySet()) { ParameterDescriptor parameterDescriptor = parameterDescriptors.get(parameterName); @@ -166,7 +170,7 @@ public List execute(WikiMacroParameters parameters, String macroContent, } } - // Verify macro content against the content descriptor. + // Verify the a macro content is not empty if it was declared mandatory. if (getDescriptor().getContentDescriptor() != null && getDescriptor().getContentDescriptor().isMandatory()) { if (StringUtils.isEmpty(macroContent)) { throw new MacroExecutionException("Missing macro content: this macro requires content (a body)"); @@ -174,7 +178,7 @@ public List execute(WikiMacroParameters parameters, String macroContent, } // Parse the wiki macro content. - XDOM xdom = null; + XDOM xdom; try { Parser parser = componentManager.lookup(Parser.class, syntaxId); xdom = parser.parse(new StringReader(this.content)); @@ -200,11 +204,13 @@ public List execute(WikiMacroParameters parameters, String macroContent, macroContext.put(MACRO_PARAMS_KEY, parameters); macroContext.put(MACRO_CONTENT_KEY, macroContent); macroContext.put(MACRO_CONTEXT_KEY, context); - Map xwikiContext = null; - Object contextDoc = null; + + Map xwikiContext; + Object contextDoc; try { Execution execution = componentManager.lookup(Execution.class); xwikiContext = (Map) execution.getContext().getProperty("xwikicontext"); + xwikiContext.put(MACRO_KEY, macroContext); // Save current context document. @@ -265,11 +271,12 @@ public String getId() } /** - * {@inheritDoc} + * @return the name of the document containing the macro class definition + * @since 2.2M1 */ - public String getDocumentName() + public DocumentName getDocumentName() { - return this.macroDocument; + return this.macroDocumentName; } /** diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManager.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManager.java index 32d6eb5a0b1e..2dbafe79a794 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManager.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManager.java @@ -17,21 +17,24 @@ * 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.wikibridge; import java.util.HashMap; import java.util.Map; +import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.bridge.DocumentName; +import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.Requirement; -import org.xwiki.component.descriptor.ComponentDescriptor; import org.xwiki.component.descriptor.DefaultComponentDescriptor; -import org.xwiki.component.logging.AbstractLogEnabled; +import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; -import org.xwiki.component.manager.ComponentRepositoryException; import org.xwiki.rendering.macro.Macro; import org.xwiki.rendering.macro.wikibridge.WikiMacro; +import org.xwiki.rendering.macro.wikibridge.WikiMacroDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroException; import org.xwiki.rendering.macro.wikibridge.WikiMacroManager; +import org.xwiki.rendering.macro.wikibridge.WikiMacroVisibility; /** * Default implementation of {@link WikiMacroManager}. @@ -39,58 +42,192 @@ * @version $Id$ * @since 2.0M2 */ -public class DefaultWikiMacroManager extends AbstractLogEnabled implements WikiMacroManager +@Component +public class DefaultWikiMacroManager implements WikiMacroManager { /** - * The {@link ComponentManager} component. + * The Root {@link ComponentManager}, used to look up specific component manager for registering + * Wiki Macros against the proper one (depending on the Macro visibility). + */ + @Requirement + private ComponentManager rootComponentManager; + + /** + * The {@link org.xwiki.bridge.DocumentAccessBridge} component. */ @Requirement - private ComponentManager componentManager; + private DocumentAccessBridge bridge; /** * Map of wiki macros against document names. This is used to de-register wiki macros when corresponding documents * are deleted. */ - private Map> wikiMacroMap = new HashMap>(); + private Map wikiMacroMap = new HashMap(); + + /** + * Internal helper class to hold a wiki macro component role hint and the wiki Macro definition itself. + */ + private static class WikiMacroData + { + /** + * @see #getHint() + */ + private String hint; + /** + * @see #getWikiMacro() + */ + private WikiMacro wikiMacro; + + /** + * @param hint see {@link #getHint()} + * @param wikiMacro see {@link #getWikiMacro()} + */ + public WikiMacroData(String hint, WikiMacro wikiMacro) + { + this.hint = hint; + this.wikiMacro = wikiMacro; + } + + /** + * @return the wiki Macro component hint + */ + public String getHint() + { + return this.hint; + } + + /** + * @return the wiki Macro definition + */ + public WikiMacro getWikiMacro() + { + return this.wikiMacro; + } + } + /** * {@inheritDoc} + * @see WikiMacroManager#hasWikiMacro(DocumentName) */ - public boolean hasWikiMacro(String documentName) + public boolean hasWikiMacro(DocumentName documentName) { return (null != wikiMacroMap.get(documentName)); } /** * {@inheritDoc} + * @see WikiMacroManager#registerWikiMacro(DocumentName, WikiMacro) */ - public void registerWikiMacro(String documentName, WikiMacro wikiMacro) + public void registerWikiMacro(DocumentName documentName, WikiMacro wikiMacro) throws WikiMacroException { - DefaultComponentDescriptor descriptor = new DefaultComponentDescriptor(); - descriptor.setRole(Macro.class); - descriptor.setRoleHint(wikiMacro.getId()); - try { - componentManager.registerComponent(descriptor, wikiMacro); - wikiMacroMap.put(documentName, descriptor); - getLogger().info( - String.format("Macro [%s] in [%s] successfully registered", wikiMacro.getId(), documentName)); - } catch (ComponentRepositoryException ex) { - getLogger().error( - String.format("Unable to register macro [%s] in [%s]", wikiMacro.getId(), documentName), ex); + WikiMacroDescriptor macroDescriptor = (WikiMacroDescriptor) wikiMacro.getDescriptor(); + + // Verify that the user has the right to register this wiki macro the chosen visibility + if (isAllowed(documentName, macroDescriptor.getVisibility())) { + + DefaultComponentDescriptor componentDescriptor = new DefaultComponentDescriptor(); + componentDescriptor.setRole(Macro.class); + componentDescriptor.setRoleHint(wikiMacro.getId()); + + try { + // Register the macro against the right Component Manager, depending on the defined macro visibility. + findComponentManager(macroDescriptor.getVisibility()).registerComponent(componentDescriptor, wikiMacro); + this.wikiMacroMap.put(documentName, + new WikiMacroData(componentDescriptor.getRoleHint(), wikiMacro)); + } catch (Exception e) { + throw new WikiMacroException(String.format("Failed to register macro [%s] in [%s]", wikiMacro.getId(), + documentName, e)); + } + } else { + throw new WikiMacroException(String.format("Unable to register macro [%s] in [%s] due to insufficient " + + "privileges", wikiMacro.getId(), documentName)); } } /** * {@inheritDoc} + * @see WikiMacroManager#unregisterWikiMacro(DocumentName) + */ + public void unregisterWikiMacro(DocumentName documentName) throws WikiMacroException + { + WikiMacroData macroData = this.wikiMacroMap.get(documentName); + if (macroData != null) { + WikiMacroDescriptor macroDescriptor = (WikiMacroDescriptor) macroData.getWikiMacro().getDescriptor(); + + // Verify that the user has the right to register this wiki macro the chosen visibility + if (isAllowed(documentName, macroDescriptor.getVisibility())) { + try { + findComponentManager(macroDescriptor.getVisibility()).unregisterComponent(Macro.class, + macroData.getHint()); + this.wikiMacroMap.remove(documentName); + } catch (Exception e) { + throw new WikiMacroException(String.format("Failed to unregister macro [%s] in [%s]", + macroData.getHint(), documentName), e); + } + } else { + throw new WikiMacroException(String.format("Unable to unregister macro [%s] in [%s] due to " + + "insufficient privileges", macroData.getWikiMacro().getId(), documentName)); + } + } else { + throw new WikiMacroException(String.format("Macro [%s] in [%s] isn't registered", + macroData.getWikiMacro().getId(), documentName)); + } + } + + /** + * @param documentName the name of the document containing the wiki macro definition + * @param visibility the visibility required + * @return true if the current user is allowed to register or unregister the wiki macro contained in the passed + * document name and with the passed visibility. Global visibility require programming rights on the + * document (to ensure they cannot be defined by standard users in a wiki farm - since only farm admins + * have programming rights in a farm). Current user and current wiki visibility simply require edit rights + * on the document. + */ + private boolean isAllowed(DocumentName documentName, WikiMacroVisibility visibility) + { + boolean isAllowed = false; + + switch (visibility) { + case GLOBAL: + // Verify that the user has programming rights since XWiki doesn't have a Wiki Farm Admin right yet + // and the programming rights is the closest to it. + if (this.bridge.hasProgrammingRights()) { + isAllowed = true; + } + break; + default: + // Verify the user has edit rights on the document containing the Wiki Macro definition + if (this.bridge.isDocumentEditable(documentName)) { + isAllowed = true; + } + } + + return isAllowed; + } + + /** + * @param visibility the visibility required + * @return the Component Manager to use to register/unregister the wiki macro. The Component Manager to use depends + * on the macro visibility. For example a macro that has the "current user" visibility must be + * registered against the User Component Manager. + * @throws ComponentLookupException if the Component Manager for the specified visibility cannot be found */ - public void unregisterWikiMacro(String documentName) + private ComponentManager findComponentManager(WikiMacroVisibility visibility) throws ComponentLookupException { - ComponentDescriptor macroDescriptor = wikiMacroMap.get(documentName); - componentManager.unregisterComponent(macroDescriptor.getRole(), macroDescriptor.getRoleHint()); - wikiMacroMap.remove(documentName); - getLogger() - .info( - String.format("Macro [%s] in [%s] successfully de-registered", macroDescriptor.getRoleHint(), - documentName)); + ComponentManager cm; + + switch (visibility) { + case USER: + cm = this.rootComponentManager.lookup(ComponentManager.class, "user"); + break; + case WIKI: + cm = this.rootComponentManager.lookup(ComponentManager.class, "wiki"); + break; + default: + cm = this.rootComponentManager; + } + + return cm; } } diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/WikiMacroEventListener.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/WikiMacroEventListener.java index 7bb8920100bc..78ef59ae327c 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/WikiMacroEventListener.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/internal/macro/wikibridge/WikiMacroEventListener.java @@ -19,13 +19,11 @@ */ package org.xwiki.rendering.internal.macro.wikibridge; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.bridge.DocumentModelBridge; import org.xwiki.bridge.DocumentName; -import org.xwiki.bridge.DocumentNameSerializer; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.Requirement; import org.xwiki.component.logging.AbstractLogEnabled; @@ -50,18 +48,6 @@ @Component("wikimacrolistener") public class WikiMacroEventListener extends AbstractLogEnabled implements EventListener { - /** - * The {@link DocumentAccessBridge} component. - */ - @Requirement - private DocumentAccessBridge docBridge; - - /** - * The {@link DocumentNameSerializer} component. - */ - @Requirement - private DocumentNameSerializer docNameSerializer; - /** * The {@link org.xwiki.rendering.macro.wikibridge.WikiMacroFactory} component. */ @@ -87,11 +73,10 @@ public String getName() */ public List getEvents() { - List events = new ArrayList(); - events.add(new DocumentSaveEvent()); - events.add(new DocumentUpdateEvent()); - events.add(new DocumentDeleteEvent()); - return events; + return Arrays.asList( + new DocumentSaveEvent(), + new DocumentUpdateEvent(), + new DocumentDeleteEvent()); } /** @@ -101,51 +86,64 @@ public void onEvent(Event event, Object source, Object data) { if (event instanceof AbstractDocumentEvent) { DocumentModelBridge document = (DocumentModelBridge) source; - DocumentName documentName = docBridge.getDocumentName(document.getFullName()); - String fullDocumentName = docNameSerializer.serialize(documentName); + DocumentName documentName = document.getDocumentName(); + + // We've decided not to log any exception raised in the XWiki logs since a failure to register or + // unregister a macro isn't a failure of the XWiki software. It's something normal, same as, for example, + // not being able to edit a page if the user doesn't have edit rights. + // The problem here is that since Macros are registered by an Event Listener there's no way defined + // to let the user know about the status. We'd need to: + // - create a status page listing the state of all macros with the last error messages + // - don't use a listener to register macros and instead have a page listing all macros with a + // register/unregister button (and thus provide visual feedback when the action fails). if (event instanceof DocumentSaveEvent || event instanceof DocumentUpdateEvent) { // Unregister any existing macro registered under this document. - if (wikiMacroManager.hasWikiMacro(fullDocumentName)) { - wikiMacroManager.unregisterWikiMacro(fullDocumentName); - } + if (unregisterMacroInternal(documentName)) { - // Check whether the given document has a wiki macro defined in it. - if (macroFactory.containsWikiMacro(fullDocumentName)) { - // Make sure the wiki macro is defined on the main wiki. - if (!fullDocumentName.startsWith("xwiki:")) { - getLogger().error("Wiki macro registration from virtual wikis are not allowed"); - return; - } + // Check whether the given document has a wiki macro defined in it. + if (macroFactory.containsWikiMacro(documentName)) { - // Attempt to create a wiki macro. - WikiMacro wikiMacro = null; - try { - wikiMacro = macroFactory.createWikiMacro(fullDocumentName); - } catch (WikiMacroException ex) { - getLogger().error(ex.getMessage()); - return; - } + // Attempt to create a wiki macro. + WikiMacro wikiMacro = null; + try { + wikiMacro = macroFactory.createWikiMacro(documentName); + } catch (WikiMacroException e) { + getLogger().debug(String.format("Failed to create wiki macro [%s]", documentName), e); + return; + } - // Check if the user has programming rights before continuing further. - // TODO: This is temporary and it's done to prevent users in a farm to be able to create wiki - // macros since they would be visible by all wikis in the farm. In the future this will be - // fixed when we introduce the notion of Realms in the Component Manager. When this happens - // we'll be able to register the macro so it's visible in the current user's realm only and - // offer an admin screen so that admins can promote user-visible macros to wiki-instance-visible - // macros. - if (!docBridge.hasProgrammingRights()) { - String errorMessage = "Unable to register macro [%s] due to insufficient privileges"; - getLogger().error(String.format(errorMessage, wikiMacro.getId())); - return; + // Register the macro. + registerMacroInternal(documentName, wikiMacro); } - - // Register macro. - wikiMacroManager.registerWikiMacro(fullDocumentName, wikiMacro); } - } else if (event instanceof DocumentDeleteEvent && wikiMacroManager.hasWikiMacro(fullDocumentName)) { - wikiMacroManager.unregisterWikiMacro(fullDocumentName); + } else if (event instanceof DocumentDeleteEvent) { + unregisterMacroInternal(documentName); + } + } + } + + private void registerMacroInternal(DocumentName documentName, WikiMacro wikiMacro) + { + try { + this.wikiMacroManager.registerWikiMacro(documentName, wikiMacro); + } catch (WikiMacroException e) { + getLogger().debug(String.format("Unable to register macro [%s] in document [%s]", + wikiMacro.getId(), documentName), e); + } + } + + private boolean unregisterMacroInternal(DocumentName documentName) + { + boolean result = true; + if (this.wikiMacroManager.hasWikiMacro(documentName)) { + try { + this.wikiMacroManager.unregisterWikiMacro(documentName); + } catch (WikiMacroException e) { + getLogger().debug(String.format("Unable to unregister macro in document [%s]", documentName), e); + result = false; } } + return result; } } diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptor.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptor.java index d3ede7e9e78c..f79a7735cc16 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptor.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptor.java @@ -50,6 +50,11 @@ public class WikiMacroDescriptor implements MacroDescriptor */ private String defaultCategory; + /** + * Whether the macro is visible in the current wiki, for the current user or global. + */ + private WikiMacroVisibility visibility; + /** * Macro content description. */ @@ -66,10 +71,12 @@ public class WikiMacroDescriptor implements MacroDescriptor * @param name the macro name * @param description macro description * @param defaultCategory default category under which this macro should be listed. + * @param visibility the macro visibility (only visible in the current wiki, for the current user or global) * @param contentDescriptor macro content description. - * @param parameterDescriptors parameter descriptors. + * @param parameterDescriptors parameter descriptors. + * @since 2.2M1 */ - public WikiMacroDescriptor(String name, String description, String defaultCategory, + public WikiMacroDescriptor(String name, String description, String defaultCategory, WikiMacroVisibility visibility, ContentDescriptor contentDescriptor, List parameterDescriptors) { this.name = name; @@ -77,6 +84,7 @@ public WikiMacroDescriptor(String name, String description, String defaultCatego this.contentDescriptor = contentDescriptor; this.parameterDescriptors = parameterDescriptors; this.defaultCategory = defaultCategory; + this.visibility = visibility; } /** @@ -135,4 +143,14 @@ public String getDefaultCategory() { return this.defaultCategory; } + + /** + * @return the visibility of the macro (ie whether the macro is visible in the current wiki, for the current user + * or global) + * @since 2.2M1 + */ + public WikiMacroVisibility getVisibility() + { + return this.visibility; + } } diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroFactory.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroFactory.java index e5bd52c869b2..0b5df887785a 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroFactory.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroFactory.java @@ -19,6 +19,7 @@ */ package org.xwiki.rendering.macro.wikibridge; +import org.xwiki.bridge.DocumentName; import org.xwiki.component.annotation.ComponentRole; /** @@ -35,8 +36,9 @@ public interface WikiMacroFactory * * @param documentName name of the document to search for a wiki macro definition. * @return true if the given document contains a wiki macro definition, false otherwise. + * @since 2.2M1 */ - boolean containsWikiMacro(String documentName); + boolean containsWikiMacro(DocumentName documentName); /** * Tries to build a {@link WikiMacro} if a definition is found on the given document. @@ -45,6 +47,7 @@ public interface WikiMacroFactory * @return a {@link WikiMacro} corresponding to the macro definition found. * @throws WikiMacroException if no macro definition is found or if an error is encountered while building * the macro. + * @since 2.2M1 */ - WikiMacro createWikiMacro(String documentName) throws WikiMacroException; + WikiMacro createWikiMacro(DocumentName documentName) throws WikiMacroException; } diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroManager.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroManager.java index 25f6293d6678..75330cc64ea3 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroManager.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroManager.java @@ -19,13 +19,12 @@ */ package org.xwiki.rendering.macro.wikibridge; +import org.xwiki.bridge.DocumentName; import org.xwiki.component.annotation.ComponentRole; /** * Component interface responsible for managing wiki macro instances. * - * TODO: Need to change all the string based document names to DocumentName. - * * @version $Id$ * @since 2.0M2 */ @@ -33,26 +32,39 @@ public interface WikiMacroManager { /** - * Registers the given {@link WikiMacro} against ComponentManager and keeps a reference to it for future reference. - * - * @param documentName name of the document which contains the wiki macro. - * @param wikiMacro the {@link org.xwiki.rendering.macro.wikibridge.WikiMacro} instance. + * Registers the given {@link WikiMacro} against the ComponentManager matching the Wiki Macro visibility defined + * (Current User, Current Wiki, Global). For example Macros defined with a "Current User" visibility are + * registered against the User Component Manager so that they are only visible from that Component Manager and not + * from other Component Manager. + *

+ * Note that the Execution Context must be set properly (the current user or the current wiki must be set) prior + * to calling this API. + * + * @param documentName the name of the document which contains the wiki macro + * @param wikiMacro the {@link org.xwiki.rendering.macro.wikibridge.WikiMacro} instance + * @exception WikiMacroException if a problem happened when registering the macro (document doesn't exist, + * not enough privilege, etc) + * @since 2.2.M1 */ - void registerWikiMacro(String documentName, WikiMacro wikiMacro); + void registerWikiMacro(DocumentName documentName, WikiMacro wikiMacro) throws WikiMacroException; /** - * Unregisters a wiki macro defined on the given document (if there is one). + * Unregisters the wiki macro defined on the given document (if there is one). * - * @param documentName name of the document which contains the wiki macro. + * @param documentName the name of the document which contains the wiki macro + * @exception WikiMacroException if a problem happened when registering the macro (document doesn't exist, + * not enough privilege, etc) + * @since 2.2.M1 */ - void unregisterWikiMacro(String documentName); + void unregisterWikiMacro(DocumentName documentName) throws WikiMacroException; /** * Utility method for querying {@link WikiMacroManager} to see if there is a {@link WikiMacro} already registered * for the given document. * - * @param documentName name of the document which contains the wiki macro. - * @return true if there is already a macro registered under the givend document name. + * @param documentName the name of the document which contains the wiki macro + * @return true if there is already a macro registered under the given document name + * @since 2.2.M1 */ - boolean hasWikiMacro(String documentName); + boolean hasWikiMacro(DocumentName documentName); } diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroVisibility.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroVisibility.java new file mode 100644 index 000000000000..f81fbab0aeeb --- /dev/null +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/main/java/org/xwiki/rendering/macro/wikibridge/WikiMacroVisibility.java @@ -0,0 +1,77 @@ +/* + * 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.macro.wikibridge; + +import java.util.HashMap; +import java.util.Map; + +/** + * The visibility of a Wiki Macro, ie whether it's visible to the current user only, to the current wiki only + * or globally. + * + * @version $Id$ + * @since 2.2M1 + */ +public enum WikiMacroVisibility +{ + /** + * Macro visible only by the current user. + */ + USER, + + /** + * Macro visible only for the current wiki. + */ + WIKI, + + /** + * Macro visible for all wikis in a farm. + */ + GLOBAL; + + /** + * Mapping between String definition of visibility and enums. The strings defined are coming from the + * Wiki Macro Class field definition for the Visibility property. + */ + private static final Map MAPPINGS = new HashMap() + { + { + put("Current User", USER); + put("Current Wiki", WIKI); + put("Global", GLOBAL); + } + }; + + /** + * Convert between a string representation of a Macro visibility and its matching enum. If no matching enum is + * found then defaults to Wiki level visibility. + * + * @param visibilityAsString the visibility as a string + * @return the enum matching the visibility defined as a string + */ + public static WikiMacroVisibility fromString(String visibilityAsString) + { + WikiMacroVisibility visibility = WIKI; + if (MAPPINGS.containsKey(visibilityAsString)) { + visibility = MAPPINGS.get(visibilityAsString); + } + return visibility; + } +} diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/RenderingTests.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/RenderingTests.java deleted file mode 100644 index 35d85fffbaab..000000000000 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/RenderingTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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; - -import java.util.ArrayList; -import java.util.List; - -import junit.framework.Test; -import junit.framework.TestCase; - -import org.jmock.Expectations; -import org.jmock.Mockery; -import org.xwiki.bridge.DocumentAccessBridge; -import org.xwiki.component.descriptor.DefaultComponentDescriptor; -import org.xwiki.component.embed.EmbeddableComponentManager; -import org.xwiki.rendering.internal.macro.wikibridge.DefaultWikiMacro; -import org.xwiki.rendering.internal.macro.wikibridge.MockWikiMacro; -import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; -import org.xwiki.rendering.macro.wikibridge.WikiMacro; -import org.xwiki.rendering.macro.wikibridge.WikiMacroDescriptor; -import org.xwiki.rendering.macro.wikibridge.WikiMacroManager; -import org.xwiki.rendering.macro.wikibridge.WikiMacroParameterDescriptor; -import org.xwiki.rendering.scaffolding.RenderingTestSuite; -import org.xwiki.test.ComponentManagerTestSetup; - -/** - * Rendering tests for wiki macros. - * - * @version $Id$ - * @since 2.0M2 - */ -public class RenderingTests extends TestCase -{ - /** - * Builds and returns a new {@link ComponentManagerTestSetup}. - * - * @return a {@link ComponentManagerTestSetup}. - * @throws Exception if an error occurs while building the test setup. - */ - public static Test suite() throws Exception - { - RenderingTestSuite suite = new RenderingTestSuite("Test Wiki Macro Bridge"); - - suite.addTestsFromResource("wikimacro1", true); - suite.addTestsFromResource("wikimacro2", true); - - ComponentManagerTestSetup testSetup = new ComponentManagerTestSetup(suite); - setUpMocks(testSetup.getComponentManager()); - - return testSetup; - } - - public static void setUpMocks(EmbeddableComponentManager componentManager) throws Exception - { - Mockery context = new Mockery(); - - // Document Access Bridge Mock - final DocumentAccessBridge mockDocumentAccessBridge = context.mock(DocumentAccessBridge.class); - DefaultComponentDescriptor descriptorDAB = - new DefaultComponentDescriptor(); - descriptorDAB.setRole(DocumentAccessBridge.class); - componentManager.registerComponent(descriptorDAB, mockDocumentAccessBridge); - - // Register mock wiki macros. - WikiMacroManager wikiMacroManager = componentManager.lookup(WikiMacroManager.class); - - // Mock wiki macro - 1. - WikiMacroParameterDescriptor param1 = new WikiMacroParameterDescriptor("param1", "This is param1", true); - WikiMacroParameterDescriptor param2 = new WikiMacroParameterDescriptor("param2", "This is param2", true); - List params = new ArrayList(); - params.add(param1); - params.add(param2); - WikiMacroDescriptor descriptor = new WikiMacroDescriptor("Mock Wiki Macro - 1", "Description", "Test", - new DefaultContentDescriptor(false), params); - WikiMacro wikiMacro = new MockWikiMacro(new DefaultWikiMacro("xwiki:Main.MockWikiMacro1", "mockwikimacro1", - true, descriptor, "This is **mockwikimacro1**", "xwiki/2.0", componentManager), componentManager); - wikiMacroManager.registerWikiMacro("xwiki:Main.MockWikiMacro1", wikiMacro); - - // Mock wiki macro - 2. - params = new ArrayList(); - descriptor = new WikiMacroDescriptor("Mock Wiki Macro - 2", "Description", "Test", - new DefaultContentDescriptor(false), params); - wikiMacro = new MockWikiMacro(new DefaultWikiMacro("xwiki:Main.MockWikiMacro2", "mockwikimacro2", - true, descriptor, "{{mockwikimacro1 param1=\"p1\" param2=\"p2\"/}}", "xwiki/2.0", componentManager), - componentManager); - wikiMacroManager.registerWikiMacro("xwiki:Main.MockWikiMacro2", wikiMacro); - - - context.checking(new Expectations() {{ - allowing(mockDocumentAccessBridge).getDocument("xwiki:Main.MockWikiMacro1"); will(returnValue(null)); - allowing(mockDocumentAccessBridge).getDocument("xwiki:Main.MockWikiMacro2"); will(returnValue(null)); - }}); - } -} diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManagerTest.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManagerTest.java new file mode 100644 index 000000000000..9967ac5e2e83 --- /dev/null +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroManagerTest.java @@ -0,0 +1,213 @@ +/* + * 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.wikibridge; + +import java.util.ArrayList; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Assert; +import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.bridge.DocumentName; +import org.xwiki.component.descriptor.DefaultComponentDescriptor; +import org.xwiki.rendering.internal.macro.wikibridge.DefaultWikiMacro; +import org.xwiki.rendering.macro.Macro; +import org.xwiki.rendering.macro.MacroManager; +import org.xwiki.rendering.macro.MacroId; +import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroException; +import org.xwiki.rendering.macro.wikibridge.WikiMacroManager; +import org.xwiki.rendering.macro.wikibridge.WikiMacroParameterDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroVisibility; +import org.xwiki.test.AbstractComponentTestCase; + +/** + * Unit tests for {@link org.xwiki.rendering.internal.macro.wikibridge.DefaultWikiMacroManager}. + * + * @version $Id: DefaultWikiMacroManagerTest.java 25429 2009-12-03 15:04:48Z vmassol $ + * @since 2.0M2 + */ +public class DefaultWikiMacroManagerTest extends AbstractComponentTestCase +{ + /** + * The {@link org.xwiki.rendering.macro.wikibridge.WikiMacroManager} component. + */ + private WikiMacroManager wikiMacroManager; + + /** + * The {@link MacroManager} component. + */ + private MacroManager macroManager; + + private Mockery mockery = new Mockery(); + + private DocumentAccessBridge mockDocumentAccessBridge; + + @Override protected void registerComponents() throws Exception + { + super.registerComponents(); + + // Document Access Bridge Mock + this.mockDocumentAccessBridge = mockery.mock(DocumentAccessBridge.class); + DefaultComponentDescriptor descriptorDAB = + new DefaultComponentDescriptor(); + descriptorDAB.setRole(DocumentAccessBridge.class); + getComponentManager().registerComponent(descriptorDAB, this.mockDocumentAccessBridge); + + this.macroManager = getComponentManager().lookup(MacroManager.class); + this.wikiMacroManager = getComponentManager().lookup(WikiMacroManager.class); + } + + @org.junit.Test + public void testRegisterWikiMacroWhenGlobalVisibilityAndAllowed() throws Exception + { + final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.GLOBAL); + + // Simulate a user with programming rights + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).getCurrentDocumentName(); will(returnValue(wikiMacro.getDocumentName())); + allowing(mockDocumentAccessBridge).getCurrentUser(); will(returnValue("dummy")); + allowing(mockDocumentAccessBridge).hasProgrammingRights(); will(returnValue(true)); + }}); + + Assert.assertFalse(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentName())); + + wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentName(), wikiMacro); + Assert.assertTrue(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentName())); + + Macro< ? > registeredMacro = macroManager.getMacro(new MacroId("testwikimacro")); + Assert.assertEquals(0, registeredMacro.compareTo(wikiMacro)); + + wikiMacroManager.unregisterWikiMacro(wikiMacro.getDocumentName()); + Assert.assertFalse(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentName())); + + Assert.assertFalse(macroManager.exists(new MacroId("testwikimacro"))); + } + + @org.junit.Test + public void testRegisterWikiMacroWhenWikiVisibilityAndAllowed() throws Exception + { + final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.WIKI); + + // Simulate a user with programming rights + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).getCurrentDocumentName(); will(returnValue(wikiMacro.getDocumentName())); + allowing(mockDocumentAccessBridge).getCurrentUser(); will(returnValue("dummy")); + allowing(mockDocumentAccessBridge).isDocumentEditable(wikiMacro.getDocumentName()); will(returnValue(true)); + }}); + + wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentName(), wikiMacro); + Assert.assertTrue(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentName())); + + Macro< ? > registeredMacro = macroManager.getMacro(new MacroId("testwikimacro")); + Assert.assertEquals(0, registeredMacro.compareTo(wikiMacro)); + } + + @org.junit.Test + public void testRegisterWikiMacroWhenUserVisibilityAndAllowed() throws Exception + { + final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.USER); + + // Simulate a user with programming rights + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).getCurrentDocumentName(); will(returnValue(wikiMacro.getDocumentName())); + allowing(mockDocumentAccessBridge).getCurrentUser(); will(returnValue("dummy")); + allowing(mockDocumentAccessBridge).isDocumentEditable(wikiMacro.getDocumentName()); will(returnValue(true)); + }}); + + wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentName(), wikiMacro); + Assert.assertTrue(wikiMacroManager.hasWikiMacro(wikiMacro.getDocumentName())); + + Macro< ? > registeredMacro = macroManager.getMacro(new MacroId("testwikimacro")); + Assert.assertEquals(0, registeredMacro.compareTo(wikiMacro)); + } + + @org.junit.Test + public void testRegisterWikiMacroWhenGlobalVisibilityAndNotAllowed() throws Exception + { + final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.GLOBAL); + + // Simulate a user without programming rights + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).hasProgrammingRights(); will(returnValue(false)); + }}); + + try { + wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentName(), wikiMacro); + Assert.fail("Should have raised an exception here"); + } catch (WikiMacroException e) { + Assert.assertEquals("Unable to register macro [testwikimacro] in [wiki = [xwiki], space = [Main], " + + "page = [TestWikiMacro]] due to insufficient privileges", e.getMessage()); + } + } + + @org.junit.Test + public void testRegisterWikiMacroWhenWikiVisibilityAndNotAllowed() throws Exception + { + final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.WIKI); + + // Simulate a user without edit rights + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).isDocumentEditable(wikiMacro.getDocumentName()); + will(returnValue(false)); + }}); + + try { + wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentName(), wikiMacro); + Assert.fail("Should have raised an exception here"); + } catch (WikiMacroException e) { + Assert.assertEquals("Unable to register macro [testwikimacro] in [wiki = [xwiki], space = [Main], " + + "page = [TestWikiMacro]] due to insufficient privileges", e.getMessage()); + } + } + + @org.junit.Test + public void testRegisterWikiMacroWhenUserVisibilityAndNotAllowed() throws Exception + { + final DefaultWikiMacro wikiMacro = generateWikiMacro(WikiMacroVisibility.USER); + + // Simulate a user without edit rights + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).isDocumentEditable(wikiMacro.getDocumentName()); + will(returnValue(false)); + }}); + + try { + wikiMacroManager.registerWikiMacro(wikiMacro.getDocumentName(), wikiMacro); + Assert.fail("Should have raised an exception here"); + } catch (WikiMacroException e) { + Assert.assertEquals("Unable to register macro [testwikimacro] in [wiki = [xwiki], space = [Main], " + + "page = [TestWikiMacro]] due to insufficient privileges", e.getMessage()); + } + } + + private DefaultWikiMacro generateWikiMacro(WikiMacroVisibility visibility) throws Exception + { + DocumentName wikiMacroDocName = new DocumentName("xwiki", "Main", "TestWikiMacro"); + + WikiMacroDescriptor descriptor = new WikiMacroDescriptor("Test Wiki Macro", "Description", "Test", + visibility, new DefaultContentDescriptor(), new ArrayList()); + DefaultWikiMacro wikiMacro = new DefaultWikiMacro(wikiMacroDocName, "testwikimacro", true, descriptor, + "== Test ==", "xwiki/2.0", getComponentManager()); + + return wikiMacro; + } +} diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroTest.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroTest.java new file mode 100644 index 000000000000..69f01fc960dc --- /dev/null +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/internal/macro/wikibridge/DefaultWikiMacroTest.java @@ -0,0 +1,144 @@ +/* + * 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.wikibridge; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.*; +import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.bridge.DocumentName; +import org.xwiki.component.descriptor.DefaultComponentDescriptor; +import org.xwiki.context.Execution; +import org.xwiki.rendering.converter.Converter; +import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroManager; +import org.xwiki.rendering.macro.wikibridge.WikiMacroParameterDescriptor; +import org.xwiki.rendering.macro.wikibridge.WikiMacroVisibility; +import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter; +import org.xwiki.rendering.syntax.Syntax; +import org.xwiki.test.AbstractComponentTestCase; + +import java.io.StringReader; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * Unit tests for {@link DefaultWikiMacro}. + * + * @version $Id$ + * @since 2.2M1 + */ +public class DefaultWikiMacroTest extends AbstractComponentTestCase +{ + private Mockery mockery = new Mockery(); + + private DocumentAccessBridge mockDocumentAccessBridge; + + private DocumentName wikiMacroDocumentName; + + /** + * The {@link org.xwiki.rendering.macro.wikibridge.WikiMacroManager} component. + */ + private WikiMacroManager wikiMacroManager; + + @Before + public void setUp() throws Exception + { + super.setUp(); + + // Document Access Bridge Mock + this.mockDocumentAccessBridge = mockery.mock(DocumentAccessBridge.class); + DefaultComponentDescriptor descriptorDAB = + new DefaultComponentDescriptor(); + descriptorDAB.setRole(DocumentAccessBridge.class); + getComponentManager().registerComponent(descriptorDAB, this.mockDocumentAccessBridge); + + this.wikiMacroDocumentName = new DocumentName("wiki", "space", "macroPage"); + + mockery.checking(new Expectations() {{ + allowing(mockDocumentAccessBridge).getCurrentDocumentName(); will(returnValue(wikiMacroDocumentName)); + allowing(mockDocumentAccessBridge).getCurrentUser(); will(returnValue("dummy")); + allowing(mockDocumentAccessBridge).hasProgrammingRights(); will(returnValue(true)); + + // This is the document containing the wiki macro that will be put in the context available in the macro + // Since we're not testing it here, it can be null. + allowing(mockDocumentAccessBridge).getDocument(wikiMacroDocumentName); will(returnValue(null)); + }}); + + this.wikiMacroManager = getComponentManager().lookup(WikiMacroManager.class); + + // Make sure the old XWiki Context is set up in the Execution Context since it's used in + // DefaultWikiMacro.execute(). + Execution execution = getComponentManager().lookup(Execution.class); + execution.getContext().setProperty("xwikicontext", new HashMap()); + } + + @org.junit.Test + public void testExecute() throws Exception + { + registerWikiMacro("wikimacro1", "This is **bold**"); + + Converter converter = getComponentManager().lookup(Converter.class); + + DefaultWikiPrinter printer = new DefaultWikiPrinter(); + converter.convert(new StringReader("{{wikimacro1 param1=\"value1\" param2=\"value2\"/}}"), + Syntax.XWIKI_2_0, Syntax.XHTML_1_0, printer); + + // Note: We're using XHTML as the output syntax just to make it easy for asserting. + Assert.assertEquals("

This is bold

", printer.toString()); + } + + /** + * When a wiki macro is used in inline mode and its code starts with a macro, that nested macro is made inline. + * In other words, the nested macro should not generate extra paragraph elements. + */ + @org.junit.Test + public void testExecuteWhenInlineAndWithMacro() throws Exception + { + registerWikiMacro("wikimacro1", "This is **bold**"); + registerWikiMacro("wikimacro2", "{{wikimacro1 param1=\"v1\" param2=\"v2\"/}}"); + + Converter converter = getComponentManager().lookup(Converter.class); + + DefaultWikiPrinter printer = new DefaultWikiPrinter(); + // Note: We're putting the macro after the "Hello" text to force it as an inline macro. + converter.convert(new StringReader("Hello {{wikimacro2 param1=\"value1\" param2=\"value2\"/}}"), + Syntax.XWIKI_2_0, Syntax.XHTML_1_0, printer); + + // Note: We're using XHTML as the output syntax just to make it easy for asserting. + Assert.assertEquals("

Hello This is bold

", printer.toString()); + } + + private void registerWikiMacro(String macroId, String macroContent) throws Exception + { + List parameterDescriptors = Arrays.asList( + new WikiMacroParameterDescriptor("param1", "This is param1", true), + new WikiMacroParameterDescriptor("param2", "This is param2", true)); + WikiMacroDescriptor descriptor = new WikiMacroDescriptor("Wiki Macro", "Description", "Test", + WikiMacroVisibility.GLOBAL, new DefaultContentDescriptor(false), parameterDescriptors); + + DefaultWikiMacro wikiMacro = new DefaultWikiMacro(wikiMacroDocumentName, macroId, true, descriptor, + macroContent, "xwiki/2.0", getComponentManager()); + + wikiMacroManager.registerWikiMacro(wikiMacroDocumentName, wikiMacro); + } +} diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/DefaultWikiMacroManagerTest.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/DefaultWikiMacroManagerTest.java deleted file mode 100644 index 66504a66aa4b..000000000000 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/DefaultWikiMacroManagerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.macro.wikibridge; - -import java.util.ArrayList; - -import org.xwiki.rendering.internal.macro.wikibridge.DefaultWikiMacro; -import org.xwiki.rendering.macro.Macro; -import org.xwiki.rendering.macro.MacroManager; -import org.xwiki.rendering.macro.MacroId; -import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; -import org.xwiki.test.AbstractXWikiComponentTestCase; - -/** - * Test case for verifying correct macro registration. - * - * @version $Id$ - * @since 2.0M2 - */ -public class DefaultWikiMacroManagerTest extends AbstractXWikiComponentTestCase -{ - /** - * The {@link WikiMacroManager} component. - */ - private WikiMacroManager wikiMacroManager; - - /** - * The {@link MacroManager} component. - */ - private MacroManager macroManager; - - /** - * A {@link DefaultWikiMacro} instance. - */ - private DefaultWikiMacro wikiMacro; - - /** - * {@inheritDoc} - */ - protected void setUp() throws Exception - { - super.setUp(); - - macroManager = getComponentManager().lookup(MacroManager.class); - wikiMacroManager = getComponentManager().lookup(WikiMacroManager.class); - - // Create a wiki macro with no parameters. - WikiMacroDescriptor descriptor = - new WikiMacroDescriptor("Test Wiki Macro", "Description", "Test", new DefaultContentDescriptor(), - new ArrayList()); - wikiMacro = - new DefaultWikiMacro("xwiki:Main.TestWikiMacro", "testwikimacro", true, descriptor, "== Test ==", - "xwiki/2.0", getComponentManager()); - } - - /** - * Tests wiki macro registration. - */ - public void testWikiMacroRegistrationAndUnregistration() throws Exception - { - String docName = "xwiki:Main.TestMacro"; - assertTrue(!wikiMacroManager.hasWikiMacro(docName)); - - wikiMacroManager.registerWikiMacro(docName, wikiMacro); - assertTrue(wikiMacroManager.hasWikiMacro(docName)); - - Macro< ? > registeredMacro = macroManager.getMacro(new MacroId("testwikimacro")); - assertEquals(0, registeredMacro.compareTo(wikiMacro)); - - wikiMacroManager.unregisterWikiMacro(docName); - assertTrue(!wikiMacroManager.hasWikiMacro(docName)); - - assertTrue(!macroManager.exists(new MacroId("testwikimacro"))); - } -} diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptorTest.java b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptorTest.java index 587deabd5ff6..52973e5656b8 100644 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptorTest.java +++ b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/java/org/xwiki/rendering/macro/wikibridge/WikiMacroDescriptorTest.java @@ -26,7 +26,6 @@ import junit.framework.Assert; -import org.junit.Test; import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; import org.xwiki.rendering.macro.descriptor.ParameterDescriptor; @@ -43,14 +42,14 @@ public class WikiMacroDescriptorTest * descriptors are passed to the Wiki Macro descriptor. This is useful for example to ensure that the * WYSIWYG editor will display the wiki macro parameter in the same order as the Wiki Macro Object order. */ - @Test + @org.junit.Test public void testGetParameterDescriptorMapInCorrectOrder() { List paramDescriptors = Arrays.asList( new WikiMacroParameterDescriptor("id1", "description1", true), new WikiMacroParameterDescriptor("id2", "description2", true)); WikiMacroDescriptor descriptor = new WikiMacroDescriptor("name", "description", "category", - new DefaultContentDescriptor(), paramDescriptors); + WikiMacroVisibility.GLOBAL, new DefaultContentDescriptor(), paramDescriptors); Map result = descriptor.getParameterDescriptorMap(); Iterator it = result.keySet().iterator(); diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/resources/wikimacro1.test b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/resources/wikimacro1.test deleted file mode 100644 index 3d9574c9e272..000000000000 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/resources/wikimacro1.test +++ /dev/null @@ -1,20 +0,0 @@ -.#----------------------------------------------------- -.input|xwiki/2.0 -.#----------------------------------------------------- -{{mockwikimacro1 param1="val1" param2="val2"/}} -.#----------------------------------------------------- -.expect|event/1.0 -.#----------------------------------------------------- -beginDocument -beginMacroMarkerStandalone [mockwikimacro1] [param1=val1|param2=val2] -beginParagraph -onWord [This] -onSpace -onWord [is] -onSpace -beginFormat [BOLD] -onWord [mockwikimacro1] -endFormat [BOLD] -endParagraph -endMacroMarkerStandalone [mockwikimacro1] [param1=val1|param2=val2] -endDocument \ No newline at end of file diff --git a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/resources/wikimacro2.test b/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/resources/wikimacro2.test deleted file mode 100644 index 96988c90bdc7..000000000000 --- a/xwiki-rendering/xwiki-rendering-macros/xwiki-rendering-macro-wikibridge/src/test/resources/wikimacro2.test +++ /dev/null @@ -1,11 +0,0 @@ -.#----------------------------------------------------- -.input|xwiki/2.0 -.# When a wiki macro is used in inline mode and it's code segment starts with an internal nested macro, that -.# nested macro should also be made inline. In other words, the nested macro should not generate extra paragraph -.# elements. -.#----------------------------------------------------- -Hello! {{mockwikimacro2/}} -.#----------------------------------------------------- -.expect|xhtml/1.0 -.#----------------------------------------------------- -

Hello! This is mockwikimacro1

\ No newline at end of file diff --git a/xwiki-rendering/xwiki-rendering-xwiki/src/test/java/org/xwiki/rendering/scaffolding/MockDocumentAccessBridge.java b/xwiki-rendering/xwiki-rendering-xwiki/src/test/java/org/xwiki/rendering/scaffolding/MockDocumentAccessBridge.java index 6981c72701a4..da9398c9bb47 100644 --- a/xwiki-rendering/xwiki-rendering-xwiki/src/test/java/org/xwiki/rendering/scaffolding/MockDocumentAccessBridge.java +++ b/xwiki-rendering/xwiki-rendering-xwiki/src/test/java/org/xwiki/rendering/scaffolding/MockDocumentAccessBridge.java @@ -284,6 +284,16 @@ public boolean isDocumentEditable(String documentName) return true; } + /** + * {@inheritDoc} + * + * @see DocumentAccessBridge#isDocumentEditable(org.xwiki.bridge.DocumentName) + */ + public boolean isDocumentEditable(DocumentName documentName) + { + return true; + } + /** * {@inheritDoc} *