Skip navigation

In my previous post I talked about getting the JivePerson object for the user and loading the list of custom streams. In this post I will walk you through the Inbox calls needed to show and manipulate the user's inbox.


To start we need to specify the initial options. Lets start by loading the first 10 unread items:

JiveInboxOptions *unreadInboxOptions = [JiveInboxOptions new];
unreadInboxOptions.unread = YES;
unreadInboxOptions.count = 10;


Then we call the server:

[communityInstance inbox:unreadInboxOptions
              onComplete:^(NSArray *inboxEntries, NSDate *earliestDate, NSDate *latestDate)
  {[self showInboxEntries:inboxEntries from:earliestDate to:latestDate];}
                 onError:^(NSError *error) {[self handleInboxError:error];}];

The inboxEntries are JiveInboxEntry objects. They are sorted by date, but they include a thread id in the entry.jive.collection parameter that can be used to group the entries.


All JiveInboxEntry items include a summary of the full content suitable for display in a compact space.

A JiveInboxEntry can represent 3 types of content: general, acclaim, and notice.

A general item represents a link to viewable content through the contentURL parameter.

An acclaim item represents a grouping of JiveInboxEntry objects. The entryCollectionId parameter is the thread id of the related entries.

Finally, a notice item is just viewable content that does not link to anything else.


You can get the content from a general inbox entry item using this call:

[communityInstance contentFromURL:entryItem.contentURL
                       onComplete:^(JiveContent *content) {[self displayContent:content];}
                          onError:^(NSError *error) {[self handleContentFromURLError:error];}];


Remember to mark the item as read:

[communityInstance markInboxEntries:@[entryItem]
                         onComplete:^{[self inboxEntriesMarkedAsRead];}
                            onError:^(NSError *error)
  {[self handleInboxEntriesMarkedAsReadError:error];}];

You can see that the entryItem is passed to the method in an NSArray. You can use this to implement a batch mark interface. This method can also be used to mark items as unread.


For consistency, you should use the unreadInboxOptions.after or unreadInboxOptions.before parameters to load additional entries. For example if loadedEarliestDate is the earliestDate from the above call:

unreadInboxOptions.before = loadedEarliestDate;
[communityInstance inbox:unreadInboxOptions
              onComplete:^(NSArray *inboxEntries, NSDate *earliestDate, NSDate *latestDate)
{[self showMoreInboxEntries:inboxEntries from:earliestDate to:latestDate];}
                 onError:^(NSError *error) {[self handleInboxError:error];}];

This would load the 10 items previous to the already loaded items.

Jive is truly an international, global company. We have fantastic customers on almost every continent, offices in the United States, Canada, Israel, and Brazil, and we recently acquired a company located in Iceland. (Yes, after some hard work, I can even pronounce their names!) However, even with this global reach, it's sometimes nice to act locally. Jive has always encouraged community involvement by its employees, and this weekend, several of us will be participating in City Camps, part of the hackforchange movement. You will find us at the CityCamp Palo Alto and CityCamp NC. Even if you are not a "hacker", there are still lots of ways to participate and get involved.

Hopefully, we'll see you at one of the city camps. Together, we can make a difference!

Happy coding!

Purposeful Places - A Focus Lens for Work

With the latest cloud release, Jive introduced Purposeful Places, which are social groups that provide a structured context for user engagement around specific topic or problem. For example, a Salesforce Dealroom lets users keep abreast of the conversation surrounding a Salesforce opportunity, by pipping in relevant Chatter posts and comments into Jive in the form of activity stream entries. Additionally, the group may also present "Tiles" for displaying the latest facts for the opportunity such as monetary value, name of the primary contact, or percentage likelihood of close. Jive enables live updates of these facts by exposing endpoints for 3rd parties to push in data asynchronously.


Integration Services & the Jive NodeJS SDK

Jive makes it easy to create these backend services responsible for pushing stream and tile updates into a Purposeful Place! We created a NodeJS SDK for kickstarting your service. On top of Node being an amazing platform for rapid development, the Jive SDK even further accelerates development.  Literally within minutes you can have an service capable of serving the UI for configuring your tiles and activity stream integrations inside a Purposeful Place, as well as push data into these integration points.


  • Imitation is the finest form of flattery. In building the jive-sdk, we've tried to copy the patterns of popular frameworks, like Express. You can use the command line tool to autogenerate templates of all the supported flavors of Tiles, as well as full integration examples with popular systems such as Salesforce. Each example is ready out of the box for use, and deployable to the cloud with the minimum fuss. Here's the transcript of setting up a working Salesforce integration. The server was ready to go in minutes.

lt-a7-120000:sfdc aron.racho$ jive-sdk create sfdc_opportunity_activity

Preparing example  sfdc_opportunity_activity /private/tmp/sfdc

... Done!

Contents of tiles directory ( /private/tmp/sfdc/tiles ):


Things you should do now, if you haven't done them already.

(1) Install dependent modules:  npm update

(2) Don't forget to edit /private/tmp/sfdc/jiveclientconfiguration.json to specify your clientID, clientSecret, clientUrl, and other important setup options!When done, run your service:

  node app.jslt-a7-120000:sfdc aron.racho$ npm install

npm WARN package.json sfdc_opportunity_activity@0.0.1 No file found!...

...└── mongodb@1.2.14 (bson@0.1.8)

lt-a7-120000:sfdc aron.racho$ node app.js

[2013-05-14 23:32:22.497] [INFO] jive-sdk - Saving new definition 'sfdc_opportunity_activity'

[2013-05-14 23:32:22.568] [INFO] jive-sdk - Running bootstrap.

[2013-05-14 23:32:22.573] [INFO] jive-sdk - Bootstrap complete.

[2013-05-14 23:32:22.573] [INFO] jive-sdk - Service started in development mode

Express server listening on port 8090

  • Customize the template through "programming by convention" -- fill in the blanks with logic specific to your application, and as long as you've satisfied the contract, the framework automatically:
    • Wires up Express routes required for configuring your integration.
    • Notifies your listeners for integration life cycle events (integration created; destroyed; updated, etc.).
    • Executes any recurrent tasks.
    • Handles persistence of required objects (built-in support for 3 flavors of persistence out of the box -- in-memory, file, and MondoDB).
  • Easily integrate with OAuth2 providers such Salesforce, using easily customizable built-in support for OAuth2 on the backend and front end. (Initially, we wired up our own OAuth2 handlers. One thing we'll be doing in the near future is incorporating an existing framework for auth, likely Passport.)
  • Build your own custom integration from scratch, or wire in just the pieces you need into your pre-existing service, using the framework's underlying API directly.



So now you've got a service - go run it on Raspberry Pi!

Right now, Purposeful Places are available in Jive's cloud release, therefore, my service must also be cloud deployed. Though there are lots of great options for hosting NodeJS services on the web (Joyent's NodeJitsu or Heroku), I wanted to deploy my integration service to the cloud in a convenient, easily debuggable environment, over which I had complete control. Enter: The Raspberry Pi!




Besides the epic geek factor of it all (no need to explain), it is amazing that with about a $60 investment (which includes shipping), we were able to create a pocket-sized integration server, capable of pushing meaningful value into multiple Jive cloud instances! Using the Pi I am able to host my service, easily modify it on the fly for rapid troubleshooting. This process is much more difficult on something like Nodejitsu, where the deploy process can take several minutes (as compared to seconds for making a change, then deploying it live to the cloud again by turning the process back on). Here is a good guide to get your own Raspberry Pi setup with NodeJS.

Easy as Pi

One of the reasons we chose NodeJS for the jive-sdk is because it is lightweight and extremely powerful. While we've still got more work to do in building out the SDK, we were able to be productive in days. I'll be talking about at this at NodePDX later this week. If you happen to be in Portland, come and check it out. Express enabled us to get a server up and running in minutes. From there, we just started filling in the pieces. Using NodeJS on the Raspberry Pi was incredibly easy--and fun. With a dash of SSH and FTP, deployment was a snap. No dealing with firewalls. No deployment hassels on Nodejitsu. It was, easy as Pi!

This is the first in a series of posts on how to use the Jive iOS SDK to make your own native Jive community iOS app.


The Jive iOS SDK is designed as an Objective-C wrapper around the existing Jive REST api. The goal is to handle the process of communicating with the server while providing an object oriented interface to the app developer. You, as the developer, should then be able to focus your expensive talents on making amazing apps that the users of your Jive community will enjoy using.


The Jive iOS SDK can be used in 2 different ways: as a black box or as a source of operations that can be queued. In the following examples I will describe using the SDK from the black box perspective. Using it as a source of operations will follow the same structure but will require the use of NSOperationQueue.


Creating a Jive iOS app using the Jive iOS SDK begins with validating the server URL. This is trivially done using the getVersionForInstance method.

    NSURL *instanceURL = [NSURL URLWithString:@""];
    [Jive getVersionForInstance:instanceURL
                     onComplete:^(JivePlatformVersion *version) {[self instanceVerified:version];}
                        onError:^(NSError *error) {[self instanceError:error];}];

A word about blocks. You can see in the above example that we use blocks for completion and error handling. In the examples used here I will implement a block by calling a method on self to indicate that you are responsible for implementing the block. All SDK methods are designed to accept nil for any block.


You will need to create a class that implements the JiveAuthorizationDelegate protocol. This protocol is designed to let you control the caching policy for the users login credentials. The only method credentialsForJiveInstance: is used by the SDK to get a credentials object. Each method will call this method and use the returned object to sign the request.

We provide a simple authorization class for basic http credentials.

- (id<JiveCredentials>)credentialsForJiveInstance:(NSURL*) url {
    return [[JiveHTTPBasicAuthCredentials alloc] initWithUsername:self.username


The next step is to setup a Jive instance.

    MyAuthorizationDelegate *communityAuthorizationDelegate = [[MyAuthorizationDelegate alloc] initWithUserName:(NSString *)userName
                                                                                                       password:(NSString *)password];
    Jive *communityInstance = [[Jive alloc] initWithJiveInstance:instanceURL


A good place to start is to get the users JivePerson object.

    [communityInstance me:^(JivePerson *me) { [self handleMeObject:me]; }
                  onError:^(NSError *error) { [self handleMeError:error]; }];


From here you can get the list of custom streams the user has created.

This call takes a JiveReturnFieldsRequestOptions which can be used to specify that only certain fields should be returned. This can be used to limit the bandwidth consumed by a request, especially requests that return NSArrays. This could be used to get just the names of the available streams and then subsequent JiveStream calls could be made just for the specific streams that the user wants to look at.

    JiveReturnFieldsRequestOptions *streamFields = [JiveReturnFieldsRequestOptions new];
    [streamFields addField:@"name"];
    [communityInstance streams:me
                    onComplete:^(NSArray *streams) { [self handleStreams:streams]; }
                       onError:^(NSError *error) { [self handleStreamsError:error]; }];

This method returns an NSArray of JiveStream objects.


A word about Options. Most of the methods in the Jive iOS SDK take an options parameter. Providing nil in this parameter will select the default options for the method.

    [communityInstance stream:streams[0]
                   onComplete:^(JiveStream *stream) { [self handleStream:stream]; }
                      onError:^(NSError *error) { [self handleStreamError:error]; }];


This also demonstrates a fundamental behavior of the SDK, when in doubt reload the object. Most Jive classes have a method to reload an object that follows the pattern of the stream method above.


Finally a note about the class hierarchy. There are 3 primary classes: JiveContent, JivePerson, and JivePlace. JivePerson represents a Jive user. JiveContent is the base of a group of content classes the users can create and view. JivePlace is the base of a group of classes that allow users to organize and control content.


Next time, the Inbox.



Important Caveat:  There is going to be a lot of talk about tags in here, but the tag features will only function properly if the app is being run in an instance which has access to the Jive Core API v3, not just v2.  What that means is, unfortunately, for anyone using this app in Jive 5.0 the tags features will not function, tags will be ignored.  This will only work for Jive 6.0+.


Another month has passed, more feedback was collected, and it was time for yet another update to the Forms App.  Here is a list of the new features that are contained in this latest update:


  • Tags, tags, and tags.  The most requested feature of the app is now available!  Tags now surface in 3 ways within the app:
    • Tags may be specified at the template level, so that any piece of content posted by the app for that template will automatically contain specific tags
    • There is a new form field type, 'tags', which will allow users to enter a set of tags within the form that will be added to the content once posted by the app
    • A default tag, 'fbldr_template' will now be added to all templates created by the app's Template Creator - no more manual intervention of any kind to get the new template out to users of the app
  • List Fields - Two new field types have been added to the template definition - 'list' and 'multi-select'
    • The 'list' field type allows a user to enter a comma-delimited list of values into a textarea, which will then be parsed and converted to a list when the content is created
    • The 'multi-select' field is just a standard HTML select box, that will allow users to select multiple values rather than just one, and will render the selections as a list in the created content
  • List Formatting - Control how a list is rendered within the created content
    • In previous versions, any list (which was only userpicker) would be rendered as a string of values, separated by a ','
    • Now, if the field is a list type (includes userpicker, and the new list and multi-select types) a list-style can be specific to control how the list is rendered in the content
      • Comma (the default, for backward compatibility), ordered (numbered list), unordered (bulleted list), none (bare list)
  • URL Field - New form field type to render a link
    • The URL field type is displayed to the user as a pair of fields - the link label and the link itself
    • Depending on what the user fills in, the link can be output a few different ways
      • User enters label only - only the label is displayed, as simple text
      • User enters URL only - the URL is the text for the link as well as the destination for the link
      • User enteres label and URL - the label is the text for the link and the URL is the destination for the link
  • Conditional HTML Output (if/else)
    • HTML template body can contain a if/else block (else is optional) that will only display in the created output if a user provides a value for an optional field
    • Looks something like this - {if $field} <something here> {else} <something else here> {/if}


And now, more detailed screenshots which show the above new features in action.


New Feature Details


Support for Tags


In the Template Creator, there is a new field labeled "Content Tags."  This is where you enter any tags that will be included on all content created from this template.  This tag field is whitespace/comma delimited, since spaces cannot be included in tag values.



When defining template fields, there is a new field type labeled "Tags for Content."  This renders a plain text field in the form, however rather than use the field value within the HTML output in the content body, the field will be split apart and added as tags to the created content.  Again, the field is split by whitespace/comma, since spaces cannot be included in tag values.



As you can see in the Content Preview below (within the Template Creator), you can see that all 4 tags, the two templates tags (first and second) that have been combined with the user-entered tags (this and that), and have been appended to the content.



When using Jive 6.0+ (required for apps to use tags), once a new template has been created by the Template Creator, a new message will be displayed.  This informs the user that the template has actually been tagged automatically and will be available to all users once the Jive search index has been updated to reflect the newly created template.  If using Jive 5.0, the app will still instruct the user to add a tag to the template, manually, so that the template will be available to the Forms App.


Also see that the proper, default, tag of 'fbldr_template' has been added to the new template.


Important Note:  Even though the default tag of 'fblr_template' has been added, which makes the template available to all app users, the tags can still be manually edited within Jive to control who has the template automatically loaded when the app starts.  See the Forms App User Guide for information on configuring the app's settings in order to control which tags, and hence which templates, are loaded by a user.  This configuration is done manually, by each user individually.


List Fields and List Formatting


The new 'list' field, labeled "List of Values," renders as a textarea and allows a user to enter any number of values, all separated by a comma.  Unlike tags, lists are separated only by commas and not by spaces/commas, so that the list of values may contain values with spaces.  The list style field controls how the list, entered by the user, is rendered in the output.



Here is how the user-entered list values would be rendered in the output as a bulleted list, which was selected in the sample above.



The new 'multi-select' field, labeled "Select Multiple Values," renders as a select list and allows a user to select any number of values from the list.  The list style field controls how the list, entered by the user, is rendered in the output.  The available list of options, from which the user may select, is contained in the "Values" field of the template form, which is just below the option for choosing the list style.



Here is how the user-selected list values would be rendered in the output as a numbered list, which was selected in the sample above.



The other 2 list display options, not shown in any example above are "comma" - the old, default list style as a single string with commas separating the values, and "none" - a list format similar to ordered and unordered but just contains no visible icon or list indicator.


URL Field


A new field type of 'link' which is labeled as "Link / URL" allows a user to enter a hyperlink to another piece of content.  This is made available to the user via 2 separate text fields in the form, one for the link label and another for the link itself.  Note that the "Default Value" for the link field is for the Label, and not for the URL.



When the user enters only the label, then the output is just a bit of simple text.  When the user enters a label and a link, then the output is the text wrapped in the provided link.  These appear very similar in the output, only difference being whether or not the label is simple text or an active HTML link.



When the user enters only the link, then that is used for both the text display label as well as the actual link.



Conditional HTML Output


Rather than defining the specification, we'll use an actual example here to show how this conditional logic works.  Note that the recommended approach for providing the HTML content for a template is to use an external document and place the document ID in the template, and NOT to enter the HTML within the Template Creator as we are doing here.  Only entering the HTML within the app in this example to simplify showing all the output within a single screenshot.


Here is the HTML body that is being supplied to the template:

{if $value}Found {$value}{else}Not Found{/if}


Essentially, this is informing the app that if the template field named 'value' has a value provided by the user, then output the string 'Found <value>', otherwise, if the user did not provide a value, then output the string 'Not Found.'


This is a sample form which contains that HTML, along with a 'title' and 'value' field defined.



When the user provides content for the 'value' field, then this will be the output.  In the above example, "This and That" was entered by the user, which is contained in the output.



When the user does not provide content for the 'value' field, then this will be the output.



Note that the else portion of the conditional is completely optional.  The following conditional statement is also valid, and will just output nothing if no field value is provided.

{if $value}Found {$value}{/if}


What's Next?


The next big feature on the roadmap is to allow users to edit existing templates.  New templates can easily be created by the app, but once a template it created, it must be modified by hand if any changes need to be made.  A future version of the app will add the ability to edit templates that have been previously created by the app.


Any questions, comments, and feedback regarding the app, either with respect to this latest version or future versions, are always welcome.  So please, ask away.

I'm incredibly excited about our ecosystem story and what is now possible on our platform. Tiles are incredibly easy to build and extremely powerful. And it's only going to get easier as we build out the jive-sdk. We are treating the development of the jive-sdk as an open source project and would welcome your participation. As we start to bring on-line additional ways of integrating with Jive, it's worth stepping back and thinking about how all the different techniques of integrating into the platform can work together to create seamless and powerful solutions for your users. I put together a quick video that illustrates several of the integration techniques in Jive and how they can work together. Here are some of the things you will see:



Tiles are used with Purposeful Place templates to present information from back end systems to the user. Each tile has a view type, which is designed by Jive and provided as part of the platform. When a tile is configured, Jive provides a Web hook where your integration middleware can post data that will be displayed. We are working on an SDK that you will be able to use as a starting point to building your own tiles.


Jive Anywhere

Jive Anywhere is the browser sidebar that sits on the right of the screen. When you open the tab, Jive Anywhere reads the page information and looks for relevant information in your Jive instance. It also allows you to create discussions based on information that you find on the Web. We have created a Jive Anywhere SDK that allows you to build "cartridges" for any site that you would like. In the video above, we combined the cartridges with Jive Apps embedded experiences. This is how we were able to include links to applications in the content created by Jive Anywhere.


Jive Apps

Hopefully, you are all familiar with Jive Apps by now. They remain the fastes and easiest way to integrate into the Jive UI. In addition the !App Experiences (a.k.a. embedded experiences), apps can provide actions to content pages and have a full screen canvas view.



The right tool for the right job

The video above highlighted three of our integration strategies, Tiles, Jive Anywhere, and Jive Apps, and showed how they can work together. In addition to these, we have the V3 REST API, the forthcoming jive-ios-sdk, and Producteev. We recognize that you need more than a hammer to build a house. Our goal is to provide developers with the most complete, the most powerful, and simply stated, the best, API and toolset of any social business platform.


Happy Coding!

Follow us on twitter @jivedev

As most of you know, we've been working to build out the jive-sdk for purposeful places using node.js. This SDK is available in our github and you can start using it now.

The other day, I tweeted about checking out nodefly and adding this to some of the samples that we've been building.

When you log in to nodefly, this is what you see:



Huh. This looks caveman simple. No offense geiko. So I figured I'd try it out and see if it really is that easy to get going. So, as the first step, I grabbed the salesforce example that we've got with the jive-sdk. This was already up and running, so I just had to plugin the nodefly helpers. I added nodefly as a dependency in the package.json, then updated app.js. Here's my app.js (for brevity I've removed some of the comments). All I had to do was add lines 1-4.

    '64eb..........ba2', //app has the key in it.
var express = require('express'),
    http = require('http'),
    https = require('https'),
    fs = require('fs'),
    jive = require('jive-sdk');
var app = express();
var failServer = function(reason) {
    console.log('FATAL -', reason );
var options = {
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem')
var startServer = function () {
    var server = https.createServer(options, app).listen( app.get('port') || 8090, function () {
        console.log("Express server listening on port " + server.address().port);
.then( function() { return jive.service.autowire() } )
.then( function() { return jive.service.start() } ).then( startServer, failServer );

Next, I started the app. Once I did, I went back to my nodefly. Sure enough, it recognized my app was running and took me right to the monitoring dashboard.



Sweet. That was easy. And simple. And AWESOME!!! Now, we're still new to node and especially to nodefly, so we don't really know how and what to monitor (aside from the obvious), or the full capability of nodefly. That said.... It took me longer to write this blog than monitor my application.


Filter Blog

By date: By tag: