| Name | Portlet, TOC, Hidden and Script macros |
|---|---|
| Vendor | Paul Rene Jorgensen (Website) |
| Authors | Paul Rene J���rgensen |
| Homepage | http://confluence.atlassian.com/display/CONFEXT/Portlet%2C+Script%2C+Hidden+and+TOC+Macros |
| Issue Management | |
| Continuous Integration | n/a |
| Categories | Content Macros |
| Most Recent Version | 2.0 |
| Availability | Confluence v2.0 to v2.10 |
| State | Stable |
| Support |
|
| License | Freeware / Open Source (BSD) |
| Price | Free |
| Release Docs | http://confluence.atlassian.com/display/CONFEXT/Portlet%2C+Script%2C+Hidden+and+TOC+Macros |
| Java API Docs | n/a |
| Download Source | n/a |
| Download JAR | portlet-os-plugin-for-v2.x.jar |
| This plugin now accessable through the Confluence Repository Client. |
Macros for table of content generation, hidden content and easy extension point for scripts and portlets.
NEW! Version for Confluence 2.x
Ported to the new 2.x API, no functional changes.
Download portlet-os-plugin-for-v2.x.jar and deploy through the admin interface.
Version for Confluence 1.4
Bugfix release. Config is now parsed through the new Confluence v2 plugin framework.
Download confluence-portlet-macros-1.4.1.jar and deploy through the admin interface.
You may also take a look at the source code to debug any problems you may have.
Version for Confluence 1.3
Download confluence-portlet-macros-java-1.3.1.jar and place into the WEB-INF/lib folder of the Confluence war file.
If you only want the TOC macro download this jar instead: confluence-toc-macro-java-1.3.1.jar
If you run into problems you could try to email me at paulrene@gmail.com. I would also appreciate if you would let me know what you're using it for.
Hidden
no parameters
This text is meant for the reader of the page. \{hidden\}This is a comment for the page editors, and will not be shown to the end user.\{hidden\} This is also for the reader.
This text is meant for the reader of the page.
This is also for the reader.
TOC
| Parameter | Values | Optional? |
|---|---|---|
| anchor | false (default) - Ignore anchors true - Render anchors |
|
| layout | list (default) - Generates the TOC as an bullet point list simple - Generated the TOC as a simple concatenated string squared - The same as simple, but adds [ ] around the names |
|
\{toc:anchor=true|layout=list\}
\\
\{anchor:one\}
h1. One heading
Some text here.
\{anchor:two\}
h2. Another heading
Some more text here.
\{anchor:tree\}
h3. Yet another heading
Even more text here.
\{anchor:four\}
h2. Whoops, I did it again
Can find anything usefull here.
The example above generates the following output
The rest of the page content here...
[ One heading ] [ Another heading ] [ Yet another heading ] [ Whoops, I did it again ]
The rest of the page content here...
Script
| Parameter | Values | Optional? |
|---|---|---|
| output | html (default) - Output from the script will be passed right thru to the page wiki - Ouput will be piped thru Confluence's wiki rendering engine. This means you can use other macros in your output, even the script and portlet macros. Behave! |
|
\{script:output=html\}
import com.atlassian.confluence.user.UserAccessor;
import bucket.container.ContainerManager;
import com.opensymphony.user.User;
UserAccessor userAccessor = (UserAccessor) ContainerManager.getInstance(). \\
getContainerContext().getComponent("userAccessor");
System.out.println("<hr><b>Dumping users and emails</b><br>");
ArrayList users = userAccessor.getUsers();
System.out.println("<TABLE CLASS=\"wikitable\">");
System.out.println("<TR>");
System.out.println("<TH>Username</TH>");
System.out.println("<TH>FullName</TH>");
System.out.println("<TH>Email</TH>");
System.out.println("<TH>Groups</TH>");
System.out.println("</TR>");
for(User user: users) {
List groupList = user.getGroups();
String groups = new String();
for(String group: groupList) {
groups += group+"<br>";
}
System.out.println("<TR>");
System.out.println("<TD>"+user.getName()+"</TD>");
System.out.println("<TD>"+user.getFullName()+"</TD>");
System.out.println("<TD>"+user.getEmail()+"</TD>");
System.out.println("<TD>"+groups+"</TD>");
System.out.println("</TR>");
}
System.out.println("</TABLE>");
\{script\}
Portlet
| Parameter | Values | Optional? |
|---|---|---|
| classname | Fully qualifed class name of class implementing the Portlet interface or extending the GenericPortlet class | |
| config | key=value, key2=value - List of key value pairs available to the portlet thru the PortletConfig object | |
The portlet macro has two modes. Either it's in scripting mode or in native mode. I'll try to explain the difference using two examples. In native mode you must compile your class(es) and put them in the app server classpath (eg. in Confluence's WEB-INF/lib path). In script mode the portlet code is inserted directly into the page between the portlet macro tags. This is useful for development / testing. Any output to System.err is displayed in a error box on top of the page in both modes. I usually develop portlets in script mode, and then compiles them and use native mode when entering production.
\{portlet:classname=org.test.portlet.TestPortlet\}
\{portlet:classname=org.test.portlet.SimpleScriptedPortlet\}
public void init(PortletConfig config) {
// Do nothing here in this example.
}
public void service(PortletRequest req, PortletResponse resp) {
PrintWriter out = resp.getWriter();
out.println("Hello, *"+req.getUser().getFullName()+"!*");
out.println("----");
out.println("\\\\");
out.println("\\\\");
out.println("[A page]");
out.println("\\\\");
out.println("\\\\");
out.println("{script:output=wiki}");
out.println("System.out.println(\"*Xyzzy*!\");");
out.println("{script}");
out.println("\\\\");
out.println("\\\\");
// Tell the container that the output from the portlet is in wiki style and should be put thru
// the Confluence rendering engine.
resp.setContentType(PortletResponse.CONTENT_TYPE_TEXT_WIKI);
}
\{portlet\}
- means I had to split the line over several lines to get the formatting pretty. When you use the code please remove the
, and stitch the line together again.
\{portlet:classname=org.test.portlet.NewsPortlet\}
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import bucket.container.ContainerManager;
import com.atlassian.confluence.pages.BlogPost;
import com.atlassian.confluence.pages.PageManager;
import com.atlassian.confluence.renderer.PageContext;
import com.atlassian.confluence.renderer.PortletConfig;
import com.atlassian.confluence.renderer.WikiStyleRenderer;
import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.spaces.SpaceManager;
import com.telenor.confluence.macros.portlet.GenericPortlet;
import com.telenor.confluence.macros.portlet.PortletConfig;
import com.telenor.confluence.macros.portlet.PortletRequest;
import com.telenor.confluence.macros.portlet.PortletResponse;
import com.telenor.confluence.utils.TextHtml;
public void init(PortletConfig config) throws PortletException {
}
SpaceManager spaceManager = (SpaceManager) ContainerManager.getInstance().getContainerContext().\\
getComponent("spaceManager");
PageManager pageManager = (PageManager) ContainerManager.getInstance().getContainerContext().\\
getComponent("pageManager");
WikiStyleRenderer renderBean = (WikiStyleRenderer) ContainerManager.getInstance().\\
getContainerContext().getComponent("wikiStyleRenderer");
SimpleDateFormat dateformat = new SimpleDateFormat("d.M.yy H:mm");
public void service(PortletRequest req, PortletResponse resp) {
ArrayList spaces = (ArrayList) spaceManager.getSpaces();
// Retrieve Blogs
ArrayList allBlogs = new ArrayList();
StringBuffer newsList = new StringBuffer();
for(int n=0;n<spaces.size();n++) {
Space space = (Space) spaces.get(n);
ArrayList blogs = (ArrayList) pageManager.getRecentlyAddedBlogPosts(5, space.getKey());
allBlogs.addAll(blogs);
if (blogs.size() > 0) {
newsList.append("<b>" + TextHtml.text2html(space.getName()) + "</b><br>\n");
for(int t=0;t<blogs.size();t++) {
BlogPost blog = (BlogPost) blogs.get(t);
newsList.append("<a href=\"/confluence" + \\
blog.getUrlPath() + "\"><small>" + TextHtml.text2html(blog.getTitle()) + "</small></a><br><small>" + \\
dateformat.format(blog.getCreationDate()) + "</small><br>\n");
}
newsList.append("<br>\n");
}
}
// Initialize Blogs sorter
Comparator dateCompare = new Comparator() {
public int compare(Object o1, Object o2) {
if ((o1 instanceof BlogPost) && (o2 instanceof BlogPost)) {
BlogPost bp1 = (BlogPost) o1;
BlogPost bp2 = (BlogPost) o2;
return bp2.getCreationDate(). \\
compareTo(bp1.getCreationDate());
}
return 0;
}
};
// Sort blogs
Collections.sort(allBlogs, dateCompare);
// Display blogs
resp.getWriter().print(displayBlogsAsNews(allBlogs, newsList));
}
public String renderContent(BlogPost blog) {
String excerptTag = "{excerpt}";
String beginTag = "{anchor:begintease}";
String endTag = "{anchor:endtease}";
PageContext ctx = blog.toPageContext();
String content = blog.getContent();
int aBegin = content.toLowerCase().indexOf(beginTag);
int aEnd = content.toLowerCase().lastIndexOf(endTag);
int eBegin = content.toLowerCase().indexOf(excerptTag);
int eEnd = content.toLowerCase().lastIndexOf(excerptTag);
int begin, end, beginTagLen, endTagLen;
if(aBegin>=0 && aBegin<eBegin) {
begin = aBegin;
beginTagLen = beginTag.length();
} else {
begin = eBegin;
beginTagLen = excerptTag.length();
}
if(aEnd>=0 && aEnd>eEnd) {
end = aEnd;
endTagLen = endTag.length();
} else {
end = eEnd;
endTagLen = excerptTag.length();
}
if (begin >= 0 && end >= 0) {
content = content.substring(begin+beginTagLen, end);
}
while(content.startsWith("\n")) {
content = content.substring(1);
}
while(content.endsWith("\n")) {
content = content.substring(0,content.length()-1);
}
return renderBean.convertWikiToXHtml(ctx, content);
}
public String insertArticle(BlogPost blog) {
StringBuffer r = new StringBuffer();
r.append("<small>" + \\
TextHtml.text2html(blog.getSpace().getName()) + ":</small><br>\n");
r.append("<b>" + TextHtml.text2html(blog.getTitle()) + "</b><br>\n");
r.append(TextHtml.text2html(renderContent(blog)) + "\n");
r.append("<A HREF=\"/confluence" + blog.getUrlPath() + "\">Read story</A>\n");
r.append("<small>[" + dateformat.format(blog.getCreationDate()) + "]</small>\n");
return r.toString();
}
public String displayBlogsAsNews(ArrayList blogs, StringBuffer newsList) {
StringBuffer out = new StringBuffer();
out.append("<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\">\n");
out.append("<TR><TD WIDTH=\"75%\" VALIGN=\"TOP\">");
out.append("<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLPADDING=\"4\" CELLSPACING=\"0\">\n");
out.append("<TR>");
int n = 0;
int count = 0;
boolean first = true;
String categoryName;
while (n < blogs.size()) {
BlogPost blog = (BlogPost) blogs.get(n);
if (first) {
out.append("<TD COLSPAN=\"2\" VALIGN=\"TOP\">" + \\
insertArticle(blog) + "</TD>\n");
first = false;
count--;
} else {
out.append("<TD WIDTH=\"50%\" VALIGN=\"TOP\">" + \\
insertArticle(blog) + "</TD>\n");
}
count++;
if (count % 2 == 0) {
out.append("</TR>\n<TR><TD COLSPAN=\"2\">"+ \\
"<IMG SRC=\"images/blank.gif\" WIDTH=1 HEIGHT=1></TD></TR>\n<TR>");
}
n++;
}
out.append("</TR>");
out.append("</TABLE></TD>");
out.append("<TD> </TD>");
out.append("<TD WIDTH=\"25%\" VALIGN=\"TOP\">");
out.append("<b>News Archive</b><hr>\n");
out.append(newsList.toString());
out.append("</TD></TR></TABLE><br><br>\n");
return out.toString();
}
\{portlet\}


Comments (12)
Aug 23, 2005
Bob Swift says:
Here is the same example as above using a wiki table with links. {script:outpu...Here is the same example as above using a wiki table with links.
{script:output=wiki} import com.atlassian.confluence.user.UserAccessor; import bucket.container.ContainerManager; import com.opensymphony.user.User; UserAccessor userAccessor = (UserAccessor) ContainerManager.getInstance().getContainerContext().getComponent("userAccessor"); ArrayList users = userAccessor.getUsers(); System.out.println("|| User ID || Full name || Email || Groups ||"); for(User user: users) { List groupList = user.getGroups(); String groups = new String(); for(String group: groupList) { groups += group + " \\\\ "; } System.out.println("| " + user.getName() + " | [~" + user.getName() + "] | [mailto:" + user.getEmail() + "] | " + groups + "|"); } {script}Jan 07, 2006
Bob Swift says:
The {script} macro causes problems with Confluence 2.x. It has been replaced wit...The {script} macro causes problems with Confluence 2.x. It has been replaced with the Beanshell Macro. The {script} macro is also defined for source compatibility. Make sure you disable the {script} macro in this plugin once you install the new support.
Feb 12, 2007
Kelly Heese says:
We have v 2.0 of this plugin installed in our Confluence v229. I can't get...We have v 2.0 of this plugin installed in our Confluence v229. I can't get any link to appear in the toc.
Just renders plain text toc. How can I get links to appear??
Feb 12, 2007
Kelly Heese says:
This didn't copy over in to my original comment above. This is the code I'm usin...This didn't copy over in to my original comment above. This is the code I'm using.
{toc:anchor=true}Feb 13, 2007
Tom Moore says:
I cannot install this plugin into Confluence 2.3.2. Get a Java exception e...I cannot install this plugin into Confluence 2.3.2. Get a Java exception error
2007-02-13 14:53:29,122 ERROR [http-8080-Processor23] [com.atlassian.plugin.DefaultPluginManager] enablePluginModules There was an error loading the descriptor 'script' of plugin 'confluence.extra.portlet'. Disabling.
java.lang.NoClassDefFoundError: bucket/container/ComponentNotFoundException
at java.lang.Class.getDeclaredMethods0(Native Method)
...
...
etc
Feb 28, 2007
Brandon Whitehead says:
I have version 2.0 of this plugin installed on Confluence 2.3.3 and the hidden m...I have version 2.0 of this plugin installed on Confluence 2.3.3 and the hidden macro is not recognized.
Mar 16, 2007
Bob Swift says:
I don't install this plugin anymore for the following reasons: Hidden macro - ...I don't install this plugin anymore for the following reasons:
Aug 01, 2007
David Goldstein says:
Bob- Thanks for the clever tip on replacing the hidden macro from this plu...Bob- Thanks for the clever tip on replacing the hidden macro from this plugin with a user macro.
Since we upgraded to 2.5.4 this plugin has never installed properly and required us to remember every restart of Confluence to go to the Plugin Repository and check the Configure checkbox to enable the plugin. Then go to the standard Plugin admin page to verify that the 'hidden' macro was enabled. Note -- if you don't use the Plugin Repos Config enable first, you can't get the 'hidden' macro enabled from just the standard Plugin admin.
So, I'm able to finally uninstall this plugin by just using the simple 'hidden' user macro instead.
Apr 16, 2008
jms says:
In case someone else is looking for the source, the table above should read Do...In case someone else is looking for the source, the table above should read
as the jar actually contains the source code.
Sep 26
Monaury Cécile says:
Hello, I have trouble with toc macro in french language (confluence 2.9):...Hello,
I have trouble with toc macro in french language (confluence 2.9): it can't deal with é à è and co !
Oct 01
Monaury Cécile says:
It's a knowing bug of 2.9.It's a knowing bug of 2.9.
Oct 01
Ben Whitehouse says:
Hello, Does the portlet macro support the JSR-168 standard i.e. could I drop so...Hello,
Does the portlet macro support the JSR-168 standard i.e. could I drop something like the Syncex Calendar portlet directly in to it?
Thanks