Skip navigation

If you've ever been curious about how Jive's web service stack works, I just created a blog post that describes it.

Ryan Rutan

Do you know Jive-Fu?

Posted by Ryan Rutan Mar 29, 2012

I'm not going to drag this announcement out, as I've been waiting far too long to bring something like this to the Jive Developer community!

 

What is Jive-Fu?

 

Jive-Fu is a reference plugin that I have put together with the assistance of some of my fellow Jivers, such as Ryan King, and even some of our community developers, such as LG .  In this plugin, I have taken a stab at wiring together all the files and configurations to help clarify the steps needed to implement a feature in the plugin framework.

Screen Shot 2012-03-29 at 1.26.22 PM.png

Goal: Illustrate connectivity between elements of the plugin framework.  It is functional for educational purposes; however,

IT IS NOT FOR USE IN PRODUCTION ENVIRONMENTS!

 

Where can I get Jive-Fu?

 

jivesoftware/jive-fu-plugin · GitHub

Note:  This plugin is currently built for Jive 5.0.2.x.


Disclaimer

This plugin hasn't been fully tested (yet), there are most certainly bugs as a result of me bringing examples from the various sources; however, the patterns and connectivity are sound between the required elements in the plugin framework.  I hope to have the plugin fully vetted in the coming months, but even in its current state, I believe it is extremely valuable.

 

Here are a list of some of the initial implementations that are provided in this first installment,

  • Struts2
    • Actions
    • URLMappers
    • Filters
    • Interceptors
    • Converters
  • DWR
  • Web Services - CXF/Spring
    • JAX-WS (SOAP)
    • JAX-RS (REST)
  • DAO - Spring JDBC Templates
  • Jive Features
    • Custom Content-Type (that's right I said it)
    • Macros
    • Widgets
    • Filters
    • Interceptors (courtest of LG .)
    • Listeners
    • ActionLink in UserBar
    • ActionLink in Tab (coming eventually, need to merge How to: Add a Tab to a Place into the plugin)
    • Upgrade Framework

 

Stay Tuned for more updates on the Jive-Fu front.  In the mean time, give it a look and feel free to share your thoughts and updates if you are so inclined.

Introduction

At igniterealtime.org we had a problem with spam. As it is a public community everyone can join and post. We did try a lot of things with little or no real success:

  • captcha for registration
  • email validation
  • no external bookmarks
  • adding 'rel=nofollow' to all external links

These steps did not really help, so we were looking for other solutions. The keyword interceptor does help to moderate messages, but too much messages need moderation now.

 

Solution

The solution was to modify the keyword interceptor to add an URL whitelist. The interceptor is now written and tested, we will likely install it. Actually we did re-use only the email alerting, everything else was replaced.  We try to get it added to Jive SBS and make it available for everyone.

 

This blog describes how to write a simple interceptor, the business logic to block or moderate potential spam links is not included.


Getting Started

Writing an Interceptor in a plugin is quite easy, one needs only a class and a property file. To get started with a lightweight dummy interceptor sources are attached. Use 7zip, tar/gzip or similar zip programs to extract the archive.

Prerequisite: Setup the SDK and the Maven build process and make sure that you can build SBS without issues.

 

Creating the GUI

The GUI is generated by Jive SBS, we only need to provide the text and save it to "src/java/beans/DummyInterceptor.properties". Replace "Dummy" with the name of your interceptor.

There are global values to identify the interceptor:

displayName=Dummy
version=1.0
shortDescription=This is a dummy interceptor
author=LG

And there are properties / values which can be enabled or disabled or text can be entered. This depends on the implementation of the business logic, we do not configure it here. Here only the informational text is specified. We can use "\" to break long lines in the property file, these do not affect the GUI. We can also add HTML codes like <b> or <br/> to adjust the GUI output a little bit.

propertyName.displayName=Title
propertyName.shortDescription=A long text.

 

It may be necessary to add a large text field to enter up to 3500 instead of 250 characters, if this is the case add also

propertyName.useLargeTextField=true

 

Let's create two properties: "interceptorEnabled" and "logString" and make use of the things we learned:

interceptorEnabled.displayName=Description
interceptorEnabled.shortDescription=Select "no" to disbale this interceptor.<br/>This \
interceptor intercepts every message.
logString.displayName=Log String
logString.shortDescription=We will log this string in the error log.
logString.useLargeTextField=true

 

Save the file to complete 30% of the interceptor development.

 

Business Logic

Required Methods

Now we need business logic to intercept messages. In the package "com.jivesoftware.community.interceptor" we will create a new class "DummyInterceptor" which implements JiveInterceptor. Of course you want to replace again "Dummy" with the name of your interceptor.

We will find three methods:

1) getTypes() returns the interceptor types which we want to intercept. Messages of type "TYPE_PRE" can be blocked but they can not be moderated. We will get "org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0" exceptions if we try to moderate content with this type while blocking works fine. So we skip this type:

return Lists.newArrayList(Type.TYPE_POST, Type.TYPE_EDIT);

2) isSystemLevel() returns false, we want to read the documentation before changing the return value to true.

3) invokeInterceptor(JiveObject jo, Type t) may throw a RejectedException, this works for all content types (according to the docs it works only for TYPE_PRE). We will ignore the Type "t".

Let's get the username and log it with the defined "logString". Then force moderation of the message.

String userName = ((JiveContentObject) jo).getUser().getUsername();
log.error("Content of " + userName + " will be moderated because of " + logString);
ModerationUtil.forceContentObjectModeration(content);

 

To be able to install the plugin also public constructor methods are needed:

public DummyInterceptor() {
}
public DummyInterceptor(int objectType, long objectID) {
}

 

Now the interceptor is 60% complete.

 

Getters and Setters

The naming of the getters and setters is almost self-explaining, for strings use getXxx/setXxx and for boolean values use isXxx/setXxx. Append the property name starting with an upper case letter. A "java.beans.IntrospectionException: Method not found: ..." exception is thrown if something is wrong with the naming.

It is important to add an annotation to get things working:

@PropertyNames( { "interceptorEnabled", "logString" })

 

Now let us add code to interact with the two properties which we did define.

 

Boolean Values

For the property "interceptorEnabled" we add "isInterceptorEnabled()" and "setInterceptorEnabled(boolean b)". We have now a boolean property, Jive will render "Yes/No" as possible values in the GUI

public final boolean isInterceptorEnabled() {
    return interceptorEnabled;
}
public final void setInterceptorEnabled(boolean interceptorEnabled) {
    this.interceptorEnabled = interceptorEnabled;
}

 

String Values

The property "logString" should allow to enter a string which we will log. So we need "getLogString()" and "setLogString(String s)".

public final String getLogString() {
    return logString;
}
public final void setLogString(String logString) {
    this.logString = logString;
}

 

Now we have completed 90% of the interceptor. It is time to complete the imports and local variables. Then compile it and everything else and deploy the new sbs.jar files.

After start-up we need to add the interceptor manually in the admin GUI. Select "Spaces", "Settings, "Interceptors", enter "com.jivesoftware.community.interceptor.DummyInterceptor" (replace again "Dummy") and click on "Add Interceptor". Now we can install and configure it. After configuration create a new discussion. After clicking on "Post Message" Jive SBS should display "Please note, your discussion will need to be approved by a moderator before it will be viewable by others.". Let us verify the log file, there we should find a line like this: "ERROR com.jivesoftware.community.interceptor.DummyInterceptor - Content of admin will be moderated because of DummyInterceptor". The user name and the end of the log message vary depending on the user and your interceptor configuration.

 

Now test and comment your code to reach 100%.

 

Hint

Avoid long running loops or web-service calls as this uses a thread in the Jive SBS application and delays the message processing for the user.

 

More useful interceptors which you may want to write could

  • verify that at least one tag is specified
  • verify that the specified tags match the content
  • automatically add a tag "question"/"discussion" for threads or "information" for documents
  • count the number of word occurrences and add tags automatically
  • enforce that the singular of words is used (eg. property instead of properties)
  • ...

In a recent exercise to put a Social Sharing toolbar on the Jive Community, I constructed some code that I felt would be useful to Community Managers and UI Designers alike, so thought I'd share.

 

Problem:

Needed to include a Social Sharing toolbar in the Community, but wanted to do this with for only "public facing content".

 

Solution:

Leverage Jive Themes, FreeMarker, and System Properties.

 

Step 1 : Determine if content is Public

Because Jive supports a number of containers, some of which are not public, it is difficult to just drop in a Social Share Toolbar blindly.  What happens if someone is in a restricted area, and they accidentally share out the file or the location?  Needless to say this would be sad.   So let's look at some FTL code you can use in Jive 5 to determine if the impression someone is viewing is "Public":

 

<#-- DETERMINES IF IMPRESSION IS PUBLIC AND RELEVANT FOR SHARING/SEARCH -->
<#assign metaPublicContent = (page.getProperty("meta.public")!"false")?matches("true") />
<#if (!metaPublicContent && ((!edit??) || (document?? && !document.documentBeingEdited)))>
    <#if (container??)>
        <#if (container.objectType == JiveConstants.COMMUNITY)>
            <#if (container.ID == 1 && pageURL?? && pageURL == "/welcome")>
                <#assign metaPublicContent=true />
            <#elseif (container.ID > 1)>
                <#assign metaPublicContent=JiveContainerPermHelper.isOpenContainer(container) />
            </#if>
        <#elseif ((container.objectType == JiveConstants.SYSTEM_CONTAINER) || (container.objectType == JiveConstants.USER_CONTAINER))> <#-- PERSONAL & SYSTEM BLOG -->
            <#assign metaPublicContent=true />
        <#else>
            <#assign metaPublicContent=JiveContainerPermHelper.isOpenContainer(container) />
        </#if>
    <#elseif (targetUser??)> <#-- USER PROFILE -->
        <#assign metaPublicContent=true />
    </#if>
</#if>
<meta name="public" content="${metaPublicContent?string}">



 

In the above, code here are some talking points:

  • Line #2  - It checks for a property (that I defined, can be anything) to see if the page has already been defined as public.  If not, the it defaults to false.
    • Note: To set this, simply add a <meta name="public" content="true" /> tag in HEAD section of the FTL you want to explicitly mark as public.
  • Line #3 - Short circuits the condition checks if override is true, or if it determines we are on n Create/Edit page
  • Line #6 - Normally, we want to ignore the Root container for pages like Actions, Activity, and Communications.  But we do want this on the Home Page!
  • Line #11 - Because blogs are Containers, but are "special" they need to broken out evaluated for System/Personal blogs when they are not bound to a Space/Group/Project
  • Line #14 - Catch all evaluation that uses the EntitlementProvider to check ANONYMOUS access to the container.  This works for Space, Social Groups (Open + Members Only), and Projects.
  • Line #16 - (optional) allow for User Profiles to be Shared, makes sense.
  • Line #20 - Having done all this calculation, it would be shame if we didn't do something with it.  We already have a server-side marker we can use, so how about create a client-side one.  So I simply put the value into a META tag for easy lookup in JavaScript.  Another side benefit, is that if you are using a search crawler...this META tag can be used as a marker (in most platforms) to treat this content differently in the index than other content indexed on the site.  Very useful information!

 

So we've figured out how to determine if content is public, now ...

 

Step 2 : Find a FreeMarker Template to house the code

If you've ever had a conversation with me about running a successful Jive platform,

Rule #1:  Minimize the number of FreeMarker templates you customize in themes.

While easy, they can add up really fast and become a nasty overhead when trying to upgrade.  That being said, I want to emphasize the word minimize, not eliminate.

My philosophy: If you have to do something you dont like, you might as well get the most value possible in return.

 

As such, to make this solution, possible I owned two different FTLs:

  • /template/decorator/default/header-meta.ftl
  • /template/decorator/default/template.ftl.

 

Owning these templates is usually a good thing, because they are relatively straight forward, which means that they tend to be easier to merge.  Also,  you get a lot of mileage out of the /template/decorator/default/*.ftl because they are included on almost every page, which gives you lots of reach with minimal ownership.

 

Instructions: By taking the code in from Step 1, I simply pasted it to the end of header-meta.ftl and saved.  Step 2,  Done!

 

Step 3 : Adding the Social Share Bar

In order to make the Social Share Bar work (in this case, we used ShareThis), we needed to put code with-in both the <HEAD> and <BODY> tags.  To do this, we leveraged the template.ftl (referenced above).  What's nice about this FTL, is that it is the glue that brings everything together.  As such, when we can see the call that includes the header-meta.ftl near the very top, which also means that the metaPublicContent variable we set (server-side FTL) is accessible now in this template.  So that means we simply add some small customizations to it, like this:

<#if (metaPublicContent?? && metaPublicContent)>

    ${JiveGlobals.getJiveProperty('socialShare.head.close.snippet')!''}

</#if>

</head>

and

<#if (metaPublicContent?? && metaPublicContent)>

    ${JiveGlobals.getJiveProperty('socialShare.body.close.snippet')!''}

</#if>

</body>


Note: I used the </head> and </body> tags as points of reference in the template.ftl.  Lines 1-4 are the customizations.


Step 4 : Add Social Share HTML Snippets to System Properties

Final step, which is simple.  Use the System Property editor in the Admin Console, and define some HTML for socialShare.head.close.snippet and socialShare.body.close.snippet.

 

That's it!  Your snippets should now be showing up on your page, and you can tweak the actual HTML at any time through the System Property interface.

 

I hope this article has been helpful and informative! =) You can use this "pattern" to add more than just Social Share functionality.  Any HTML Snippet that you want to exist across the site for public content (or all content) can be instrumented using a similar pattern.  I would urge you to make a separate property per use-case, just to be safe.  Let me know what you think, and if you have a solution you'd like to share ... let me know and I can give you access rights to blog here!

Historically, users have been able to download the source code through the Jive Community for any and all versions. With Jive 5, this distribution model has changed. The source code will still be distributed to customers, but we will now be maintaining the source code via our Maven repositories and builds. Please keep in mind that future releases of 4.5's source code will still be accessible from the My Account page. This change only pertains to Jive 5 and on.

 

The biggest change here is that we're no longer releasing Ant build scripts with the source code (for Jive 5 and on). Over the past few years, we've transitioned to Maven completely for our development work internally across all departments and it made sense to fully commit to Maven for the customer releases of the source code as well. We couldn't account for every use case with the previous source distro model, so if you're transitioning to Maven and find that you're not able to do something that you were able to previously, please file a support ticket and we'll work with you to find an analogue or another workaround. We'll also continue to publish documents in Developers to help ease the transition (i.e. Pulling Source Code from Maven Repository)

 

Thanks!

Vinh

Support Manager