Commits

Theresa Henze committed 89cec4a

Initial import

Comments (0)

Files changed (12)

+.project
+.settings
+target
+.classpath
+.directory
+.project
+bin/
Empty file added.
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.example.plugin.confluence</groupId>
+    <artifactId>macromagic</artifactId>
+    <version>1.0</version>
+
+    <organization>
+        <name>Example Company</name>
+        <url>http://www.example.com/</url>
+    </organization>
+
+    <name>macromagic</name>
+    <description>This is the com.example.plugin.confluence:macromagic plugin for Atlassian Confluence.</description>
+    <packaging>atlassian-plugin</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.10</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.confluence</groupId>
+            <artifactId>confluence</artifactId>
+            <version>${confluence.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- WIRED TEST RUNNER DEPENDENCIES -->
+        <dependency>
+            <groupId>com.atlassian.plugins</groupId>
+            <artifactId>atlassian-plugins-osgi-testrunner</artifactId>
+            <version>${plugin.testrunner.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.2.2-atlassian-1</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.atlassian.maven.plugins</groupId>
+                <artifactId>maven-confluence-plugin</artifactId>
+                <version>${amps.version}</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <productVersion>${confluence.version}</productVersion>
+                    <productDataVersion>${confluence.data.version}</productDataVersion>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <confluence.version>4.3.7</confluence.version>
+        <confluence.data.version>4.3.7</confluence.data.version>
+        <amps.version>4.1.6</amps.version>
+        <plugin.testrunner.version>1.1.1</plugin.testrunner.version>
+    </properties>
+
+</project>

src/main/java/com/example/plugin/confluence/TeaserItemMacro.java

+package com.example.plugin.confluence;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.velocity.VelocityContext;
+
+import com.atlassian.confluence.content.render.xhtml.ConversionContext;
+import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext;
+import com.atlassian.confluence.macro.Macro;
+import com.atlassian.confluence.macro.MacroExecutionException;
+import com.atlassian.confluence.renderer.radeox.macros.MacroUtils;
+import com.atlassian.confluence.util.velocity.VelocityUtils;
+import com.atlassian.renderer.RenderContext;
+import com.atlassian.renderer.v2.RenderMode;
+import com.atlassian.renderer.v2.macro.BaseMacro;
+import com.atlassian.renderer.v2.macro.MacroException;
+
+/**
+ * Macro zum Einfügen von Teasern mit Bildern zum Auflockern des Inhalts. Sollte
+ * innerhalb eines Section/Column-Container stehen.
+ * 
+ * @example {section} {column}
+ *          {teaser-item:icon=icon-name|title=Titel}lorem ipsum{teaser-item} ...
+ *          {column} {section}
+ * @author Lars-Erik Kimmel / Thomas Zobel
+ */
+public class TeaserItemMacro extends BaseMacro implements Macro {
+	private final Logger logger = Logger.getLogger(TeaserItemMacro.class);
+
+	private final String TEASER_TEMPLATE = "vm/teaseritem.vm";
+	private final Set<String> SIZES = new HashSet<String>(Arrays.asList(new String[] { "small", "medium", "large" }));
+	private final String DEFAULT_SIZE = "small";
+	private final Set<String> COLORS = new HashSet<String>(Arrays.asList(new String[] { "red", "grey" }));
+	private final String DEFAULT_COLOR = "red";
+
+	@SuppressWarnings("rawtypes")
+	@Override
+	public String execute(final Map params, final String body, final RenderContext renderContext) throws MacroException {
+		VelocityContext contextMap = new VelocityContext(MacroUtils.defaultVelocityContext());
+
+		putStringContextVar(contextMap, params, "size", SIZES, DEFAULT_SIZE);
+		putStringContextVar(contextMap, params, "color", COLORS, DEFAULT_COLOR);
+
+		// put title into map
+		contextMap.put("title", params.get("title"));
+
+		// put parts from body into map
+		putBodyPartsIntoMap(body, contextMap);
+
+		// check new tab option
+		if ("true".equals(params.get("newTab"))) {
+			contextMap.put("newTab", "open_new_window");
+		}
+
+		return VelocityUtils.getRenderedTemplate(TEASER_TEMPLATE, contextMap);
+	}
+
+	@Override
+	public String execute(final Map<String, String> params, final String body, final ConversionContext cc)
+			throws MacroExecutionException {
+		try {
+			DefaultConversionContext defaultConversionContext = (DefaultConversionContext) cc;
+			RenderContext renderContext = defaultConversionContext.getPageContext();
+			return execute(params, body, renderContext);
+		}
+		catch (MacroException e) {
+			throw new MacroExecutionException(e);
+		}
+	}
+
+	private void putBodyPartsIntoMap(final String body, final VelocityContext contextMap) {
+		logger.debug("Raw body: " + body);
+		String finalBody = extractImageFromBody(body, contextMap);
+		finalBody = extractLinkFromBody(finalBody, contextMap);
+
+		finalBody = StringUtils.trimToEmpty(finalBody);
+		logger.debug("New body: " + finalBody);
+		contextMap.put("body", finalBody);
+	}
+
+	protected String extractLinkFromBody(String body, final VelocityContext contextMap) {
+		Matcher linkMatch = Pattern.compile("<a.*?</a>").matcher(body);
+		if (linkMatch.find()) {
+			final String link = linkMatch.group(0);
+			// remove link from body
+			body = body.replace(link, "");
+
+			// get href value from link
+			Matcher hrefMatch = Pattern.compile("href=\"(.*?)\"").matcher(link);
+			if (hrefMatch.find()) {
+				contextMap.put("url", hrefMatch.group(1));
+			}
+		}
+
+		return body;
+	}
+
+	protected String extractImageFromBody(String body, final VelocityContext contextMap) {
+		Matcher imageMatch = Pattern.compile("<img.*?>").matcher(body);
+		if (imageMatch.find()) {
+			final String image = imageMatch.group(0);
+			// extract image from body
+			body = body.replace(image, "");
+
+			// get src value from image
+			Matcher srcMatch = Pattern.compile("src=\"(.*?)\"").matcher(image);
+			if (srcMatch.find()) {
+				contextMap.put("imagesrc", srcMatch.group(1));
+			}
+		}
+		else {
+			contextMap.put("image", "");
+		}
+		return body;
+	}
+
+	@Override
+	public BodyType getBodyType() {
+		return BodyType.RICH_TEXT;
+	}
+
+	@Override
+	public OutputType getOutputType() {
+		return OutputType.BLOCK;
+	}
+
+	@Override
+	public boolean isInline() {
+		return false;
+	}
+
+	@Override
+	public boolean hasBody() {
+		return true;
+	}
+
+	@Override
+	public RenderMode getBodyRenderMode() {
+		return RenderMode.allow(RenderMode.F_LINKS | RenderMode.F_LINEBREAKS | RenderMode.F_MACROS
+				| RenderMode.F_MACROS_ERR_MSG | RenderMode.F_PHRASES);
+	}
+
+	@Override
+	public boolean suppressMacroRenderingDuringWysiwyg() {
+		return true;
+	}
+
+	@Override
+	public boolean suppressSurroundingTagDuringWysiwygRendering() {
+		return false;
+	}
+
+	/**
+	 * Setzt einen Wert aus einem Enum-Type-Parameter, wenn nicht vorhanden wird ein
+	 * Default-Wert gesetzt.
+	 * 
+	 * @param contextMap
+	 *          Context
+	 * @param params
+	 *          Makro-Parameter
+	 * @param paramname
+	 *          Parametername
+	 * @param set
+	 *          Enum-Set
+	 * @param def
+	 *          Default-Wert
+	 */
+	public static void putStringContextVar(final VelocityContext contextMap, final Map params, final String paramname,
+			final Set set, final String def) {
+		Object p = params.get(paramname);
+		if (set.contains(p)) {
+			contextMap.put(paramname, p);
+		}
+		else {
+			contextMap.put(paramname, def);
+		}
+	}
+}

src/main/resources/atlassian-plugin.xml

+<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2">
+    <plugin-info>
+        <description>${project.description}</description>
+        <version>${project.version}</version>
+        <vendor name="${project.organization.name}" url="${project.organization.url}" />
+        <param name="plugin-icon">images/pluginIcon.png</param>
+        <param name="plugin-logo">images/pluginLogo.png</param>
+    </plugin-info>
+
+    <!-- add our i18n resource -->
+    <resource type="i18n" name="i18n" location="macromagic"/>
+    
+    <resource type="download" name="images/" location="images">
+        <param name="content-type" value="image/png"/>
+    </resource>
+    
+	<xhtml-macro name="teaser" class="com.example.plugin.confluence.TeaserItemMacro" key="teaser-xhtml">
+	    <!-- icon="/images/teaser-icon.png" documentation-url="" -->
+		<category name="confluence-content" />
+		<parameters>
+		    <!-- 
+			<default>
+			    <option key="showValueInPlaceholder" value="true"/>
+			</default>
+			 -->
+			<parameter name="size" type="enum" required="true" default="small">
+				<value name="small" />
+				<value name="medium" />
+				<value name="large" />
+				<!-- 
+				<option key="showKeyInPlaceholder" value="false"/>
+				<option key="showValueInPlaceholder" value="true"/> -->
+			</parameter>
+			<parameter name="title" type="string"><!-- 
+				<option key="showKeyInPlaceholder" value="false"/>
+				<option key="showValueInPlaceholder" value="true"/> -->
+			</parameter>
+			<parameter name="newTab" type="boolean"><!-- 
+				<option key="showKeyInPlaceholder" value="false"/>
+				<option key="showValueInPlaceholder" value="true"/> -->
+			</parameter>
+		</parameters>
+		<property-panel>
+		    <spacer/>
+		    <button id="Small" label="S"/>
+		    <button id="Medium" label="M"/>
+		    <button id="Large" label="L"/>
+		    <spacer/>
+		</property-panel>
+	</xhtml-macro>
+	
+	<!-- This is the legacy definition for the same macro for Confluence <= 3.x. We need it to have the macro work within wiki-markup like {teaser-item}..{teaser-item} -->
+	<macro name="teaser" class="com.example.plugin.confluence.TeaserItemMacro" key="teaser">
+		<category name="confluence-content" />
+		<parameters>
+			<parameter name="size" type="enum" required="true" default="small">
+				<value name="small" />
+				<value name="medium" />
+				<value name="large" />
+			</parameter>
+			<parameter name="title" type="string" />
+			<parameter name="newTab" type="boolean" />
+		</parameters>
+	</macro>
+	
+    <web-item key="editor-featured-macro-teaser-item" name="Insert Menu Link - Box Macro" section="system.editor.featured.macros.default" weight="11">
+        <description>Displays an insert box macro link in the tinymce insert menu</description>
+        <label key="com.example.plugin.confluence.macromagic.teaser.label">Box</label>
+        <link linkId="teaser"/>
+		<icon height="16" width="16">
+		    <!-- icon als css angeben -->
+		    <link>/images/sm-plugin-logo.png</link>
+		</icon>
+    </web-item>
+
+    <web-resource name="Box Resources" key="macroeditor-teaser">
+        <context>editor</context>
+        <!-- <dependency>com.atlassian.confluence.tinymceplugin:editor-resources</dependency>-->
+        <resource type="download" name="editor-resource-teaser.js" location="js/editor-resource-teaser.js"/>
+    </web-resource>
+    
+</atlassian-plugin>

src/main/resources/css/macromagic.css

Empty file added.

src/main/resources/images/pluginIcon.png

Added
New image

src/main/resources/images/pluginLogo.png

Added
New image

src/main/resources/js/editor-resource-teaser.js

+(function() {
+	
+	var getMacroBody = function(macroDiv) {
+        /**
+         * serialize() to ensure that any bogus tinymce tags + dirty browser markup are cleaned up.
+         * macro node should be cloned before serialization, as we don't want serialization tampering with the actual DOM element.
+         */
+        var macroBodyNode = AJS.$("td.wysiwyg-macro-body", macroDiv).clone()[0];
+        var macroBodyHtml = AJS.Rte.getEditor().serializer.serialize(macroBodyNode, {
+        	forced_root_block: false // Prevent serialize from wrapping in a <p></p>
+        });
+        return macroBodyHtml;
+	}
+	
+	var getCurrentParams = function(macroDiv) {
+        var currentParams = {};
+        if (macroDiv.attr("data-macro-parameters")) {
+            AJS.$.each(macroDiv.attr("data-macro-parameters").split("|"), function(idx, item) {
+                var param = item.split("=");
+                currentParams[param[0]] = param[1];
+            });
+        }
+        return currentParams;
+	}
+	
+    var updateMacro = function(macroNode, param) {
+        var $macroDiv = AJS.$(macroNode);
+        AJS.Rte.getEditor().selection.select($macroDiv[0]);
+        AJS.Rte.BookmarkManager.storeBookmark();
+
+        var currentParams = getCurrentParams($macroDiv);
+        currentParams["size"] = param;
+        var macroBody = getMacroBody($macroDiv);
+        
+        var macroRenderRequest = {
+            contentId: Confluence.Editor.getContentId(),
+            macro: {
+                name: "teaser",
+                params: currentParams,
+                defaultParameterValue: "",
+                body : macroBody
+            }
+        };
+
+        tinymce.confluence.MacroUtils.insertMacro(macroRenderRequest);
+    };
+
+    AJS.Confluence.PropertyPanel.Macro.registerButtonHandler("Small", function(e, macroNode) {
+        updateMacro(macroNode, "small");
+    });
+
+    AJS.Confluence.PropertyPanel.Macro.registerButtonHandler("Medium", function(e, macroNode) {
+        updateMacro(macroNode, "medium");
+    });
+
+    AJS.Confluence.PropertyPanel.Macro.registerButtonHandler("Large", function(e, macroNode) {
+        updateMacro(macroNode, "large");
+    });
+ 
+})();
+
+
+AJS.bind("init.rte", function() { 
+//	AJS.MacroBrowser.setMacroJsOverride('teaser', {opener: function() {
+//		AJS.log("DUMMY OPENER");
+//	    var popup = new AJS.Dialog(860, 530);
+//        popup.show();
+//	}})
+});

src/main/resources/macromagic.properties

+#put any key/value pairs here
+my.plugin.name=Macro Magic
+
+com.example.plugin.confluence.macromagic.teaser.desc                   = Macro for adding a box.
+com.example.plugin.confluence.macromagic.teaser.label                  = Box
+com.example.plugin.confluence.macromagic.teaser.param.newTab.label     = Open link in new tab/window (subject to the settings of your browser).
+com.example.plugin.confluence.macromagic.teaser.param.size.label       = Box size
+com.example.plugin.confluence.macromagic.teaser.param.title.label      = Title

src/main/resources/vm/teaseritem.vm

+<div class="teaser-frame teaser-frame-$!size">
+	#if ($size != "small")
+		<div class="teaser-top"></div>
+	#end
+	<div class="teaser teaser-$!size teaser-$!color">
+		#if ($size != "small")
+			#if ($image != "")
+				<img src="$!imagesrc" />
+			#end
+			<div class="teaser-title">$!title</div>
+			<div class="teaser-content">$!body</div>
+		#end
+		<div class="teaser-arrow">
+			#if ($size == 'small')
+				$!title 
+			#else 
+				$i18n.getText("more.text") 
+			#end
+		</div>
+		#if 
+			($size == "large")<div class="clear">&nbsp;</div>
+		#end
+		<a class="teaser-link $!newTab" href="$!url">$!title</a>
+	</div>
+</div>

src/test/resources/atlassian-plugin.xml

+<atlassian-plugin key="${project.groupId}.${project.artifactId}-tests" name="${project.name}" plugins-version="2">
+    <plugin-info>
+        <description>${project.description}</description>
+        <version>${project.version}</version>
+        <vendor name="${project.organization.name}" url="${project.organization.url}" />
+    </plugin-info>
+
+    <!-- from our base plugin -->
+    <component-import key="myComponent" interface="com.example.plugin.confluence.MyPluginComponent"/>
+
+    <!-- from the product container -->
+    <component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" />
+    
+</atlassian-plugin>