Skip navigation

When awesome developer tips are discovered, I find that I cant blog about them fast enough.  So to try and break that tradition, I'd like to introduce to you an amazing tool/service that was recently shared with me called ngrok.

 

What is ngrok

overview.png

  • Creates a tunnel from the public internet (http://subdomain.ngrok.com) to a port on your local machine. You can give this URL to anyone to allow them to try out a web site you're developing without doing any deployment.
  • ngrok captures all traffic through the tunnel. It displays information about the HTTP traffic for your inspection. Raw request/response bytes, parsed headers and form data, JSON/XML syntax checking and more are included.
  • Developing services which consume webhooks can be challenging if the hooks are labor-intensive to generate. Use ngrok's replay request feature to iterate quickly on a new feature without switching contexts to generate new requests.

 

How to Leverage with Jive Add-On Development

For people who choose not to, or cannot use, cloud services like Nitrous.IO (see: Rapid Jive Development in the Cloud with Nitrous.IO), and would prefer to use their local machine for all of their development needs, then ngrok is the tool for you.

 

  1. ngrok - secure introspectable tunnels to localhost  ... it's FREE (you can use your GitHub account for identity if you'd like)
    • Note:  While FREE, the project does take donations ... so if you find this tool valuable, please donate what you can.  We at Jive will be taking note of its popularity through JiveWorld and make a donation accordingly.
  2. Install the client executable to your local environment.  Support for Windows, Linux/(ARM) and Mac.
  3. Follow the instructions on your dashboard for first-time setup and use.
  4. Use the xxxxxxxx.ngrok.com url and port provided at runtime and use it as the the meta.json & definition.json file(s) in your Jive add-on.
    • Did we mention it supports HTTPS as well?
  5. Bundle your Jive add-on, upload to your Jive cloud environment and watch the network traffic flow to your local development environment!
  6. When you are done needing your tunnel....simply Ctrl+C/kill the ngrok process to close your tunnel

Bonus:  ngrok is a client utility that runs on your local machine.  Simply launch the tool when needed, and turn it off when it is not in use.  You are in control of the tunnel, and it should work through most corporate firewalls without the need for additional configuration.

 

2xBonus:  Since ngrok is a network-level tool, it is application agnostic, which means that its use is agnostic to any Jive add-on tooling, including (but not limited to) the Jive SDK for node.js, Java or .NETAny add-on service running on any port of your local machine can be retro-fitted into the cloud environment for testing.

 

 

There are a ton of great features in the ngrok tool, including its web replay and general logging of network traffic.  When doing cloud development, this tool is definitely going to make a difference!

 

What do you think of ngrok?  Do you think it will help?  If so, share your thoughts and intentions to let us know how it is making a difference in your Jive development projects.

Thank you to everyone who took the time to register and/or attend yesterday's webinar!  In total, we had over 100+ people register, and 50+ attend.  We are definitely working on our formula on how to improve the value of these webinars to increase attendance and help build our developer eco-system.  Please feel free to leave comments in the blog post below, or keep an eye out for discussions calling out for feedback/speaker requests (such as: What Spotlight Presentation(s) Do You Want at the Q3-2014 Jive Developer Webinar? ).

JiveWorld14 - Jive Developer Conference

When:  October 21-23, 2014

Where:  Cosmopolitan - Las Vegas, NV

  • More Session
  • More Hacking
  • More Beer

If you are a developer, architect or just excited by technology ... you do not want to miss this conference!

Register Today

 

Dont forget to start earning your Developer Pints!
From Points to Pints in the 2014-15 Jive Developer Program

 

Q3-2014 Jive Developer Webinar Materials

Below you will find links to the recording along with questions and feedback obtained during the webinar.

Attached you will find the presentation slides.

Dont Forget to Try Out Nitrous.IO with the Jive SDK!

Click here to build your FREE Jive Developer Cloud IDE + Sandbox

 

For any questions about this webinar, you can use the comments (below) or post a question in the Developer community.

Screen Shot 2014-09-24 at 11.01.05 AM.png

Click here to play the WebEx Recording

For those who cant use WebEx, we upload an MP4 here.
(Note: we ran into an issue with codec from WebEx, during the ARF conversion, so the video quality is poor, but audio is clear).

 

Questions

The following were questions asked during the webinar:

  • Q: What is the URL for the Nitrous.io w/Jive SDK Instructions
  • Q: Is there a way to retrieve a list of users from jive?
  • Q: Is there any new about themes customization and development?
    • A: Jive continues to invest in the Theme Wizard tool that allows customers to deploy rich themes without the need for doing custom theme development.  In terms of the Theme Editor in the Admin Console, there are currently no items on the roadmap except any relevant bugs that might be reported.
  • Q: Is it possible to customize a plugin templates file in a custom jive theme ?
    • A: The Theme Editor (not Wizard) allows you to edit known theme Jive files.  Any additional files in the theme, or from a plugin, are not editable via the Admin Console editor.
  • Q: Can you recommend prerecorded webinars and training documents for people just getting started with Jive development?
  • Q: IIs there any plan in the roadmap to allow for easier packaging or deployment of Message Templates?
    • A: xWhile technically possible to inject anything into a JAR file, the outcome you are trying to
  • Q: Is there any jive developer kind of certification being studied?
    • A: This is a discussion that is on-going, and any updates on this subject will definitely be shared in the Developer Community prominently.  Unfortunately, we do not have any timelines or milestones to share at this time.
  • Q:  I am currently looking at the databases for the first time in cloud admin for my community. Will I be able to download any data from Jive as far as users, analytics, etc.?
  • Q: Any update on including a document hard-delete api?
    • A: I've marked this request again for internal consideration.  It is currently working through the process; however, given how new this request is... I do not expect anything in this upcoming cloud cycle, but will need to wait to hear back from engineering on the priorities.
  • Q: Is there a way to deploy add-ons without requiring the registration call?
  • Q: Are there plans to allow us to put OpenSocial gadgets on the Overview page?
    • A: That feature is under consideration.    It's also worth noting that with the new Pages feature ... Tiles can make their way to custom pages.  While not exactly a full 1:1 feature

 

Follow-Ups

  • Q:  derekh44 - Are there any enhancements to tag management?
    • A: I'm following up with our Producteev and Core product managers internally to see what they say about this one.  Will ask them to respond via the comments or directly to you.  Stay tuned.
  • Q:Jens Goldhammer - Is Producteev planned for hosting on premise?
    • A: This is a question that I think we should raise in the Producteev space.
  • Q:  John Reynolds Is there any plan in the roadmap to allow for easier packaging or deployment of Email/Message Templates?
    • A:  I'm following up with internal product management on this one.  To my knowledge there are no planned roadmap items for this functionality.
  • Q: Is there a way to interact with Sharepoint other than having to install a solution on the Sharepoint server side ?
    • A: I'm following up with internal product management on this one.  In the meantime, I would perhaps raise any detailed questions in the (Archived) Jive for SharePoint community.

Hi Jivers,

Recently we had a requirement from group admins to enable them to add user's as email followers to a group (associate group with user's email enabled stream so that they receive communication in gmail).

We had this admin essential add-on by Ryan Rutan which allowed us to add user as member, not as a email follower though.

We could extend this add-on to fit our requirements. And also could utilize system webhooks to enhance this.

 

 

The customization and google groups

We have google groups integrated with jive using custom streamonce. What this means is we have a two way sync between jive and google groups.

People can now start discussion on jive or google group and it reaches people living in both jive and gmail world.

 

Now jive has its own email notification system and email preferences.

Due to the integration we wanted the email prefs in sync between google and jive.

When a user joins a jive group as member, he starts following group in Connections, which by default is not email enabled.

People have to either enable email for this stream or create a new email enabled stream and follow group in that stream to get emails.

Complex. We simplified this for our users.

 

Our Approach: An email enabled stream for all users

 

First we decided to create an email enabled stream for all existing users and also for every new user account using webhooks.

And then if user followed a group in this (or any other) email enabled stream, he gets added to the corresponding google group.

If he unfollows group from email enabled stream, then he gets removed from the google group.

 

If he follows/un-follows a group from an non email enabled stream, nothing happens on google group.

Our java plugin has the google logic right now which will be moved to add-on soon.

 

Jive's system webhooks notifies about every new user account created/enabled on jive.

I used this trigger to create a new email stream.

 

Webhooks based logic

Before getting into changes to Ryan's app, here's what I did with webhooks initially

 

Create the example webhook by running command "jive-sdk create example-webhooks".

In this example go to explicit_routes.js in folder services-->webhooks-->backend-->routes.

In exports.postWebhooks check forr following activity

 

if(activity['verb'] === "jive:user_account_created" || activity['verb'] === "jive:user_account_enabled"){.....}

 

This event gives me api url for the new user account created.

var api_url = activity['object']['id'].

Now is the time to create new stream.

 

var postRequest = {
            'authz': "signed",
            url: api_url+"/streams",
            method:'POST',
            'auth': env.auth,
            body:'{"name":"Email Watches","source":"custom","receiveEmails":"true"}'
        };

var request = require('request');
request(postRequest, function (error, response, body){......});

I used this same api call to create stream for existing users too.

 

Associating group with user's Email watches stream

You all would have already check Ryan Rutan's admin essential add-on available at jiveapps/addons/admin-essentials at master · jivesoftware/jiveapps · GitHub.

 

Check method clickPickerAddMembers() in  group-addmembers.js in this code.

The method looks like this

 

clickPickerAddMembers : function() {
        var appObj = this;
        var batchRequests = osapi.newBatch();

        $("div.selected-user span.userid").each(function( index ) {
            batchRequests.add(
                $(this).attr("pos"),
                appObj.currentCtx.createMember({
                    "person" : $(this).text(),
                    "state" : "member"
                },
                {
                    "fields" : "username"
                }
                )
            );
        });

        batchRequests.execute(appObj.handlePickerAddMembers);
    },

 

This adds user as member in the group.

This is where I changed the logic

 

Here is my method which uses jive's connects api to call my node service

 

 

clickPickerAddMembers : function() {
        var appObj = this;

        var batchRequests = osapi.newBatch();
        $("div.selected-user span.userid").each(function( index ) {
            batchRequests.add(
                $(this).attr("pos"),
                osapi.jive.connects.post({
                    authz:"signed",
                    alias: "forceAddUserService",
                    headers : { "Content-Type" : "application/json" },
                    format: 'json',
                    "body": {"groupid": $("#currentGroup").attr("data-groupid") ,"userid":$(this).text()}
                })
            );
        });

        batchRequests.execute(appObj.handlePickerAddMembers);
    },

Now my node.js service has a route to accept this request params groupid and userid.

Using webhooks code I already have Email watches for every user (a user might decide to delete this though which my app detects and notifies about).

 

Now that the control is on my node.js service. I use jive REST api do following stuff :

  • Add user to the group as member (this is what Ryan's app was doing using javascript api)'
  • Checking if user has the email enabled email watches stream.
  • Associating the group with user's email watches stream
  • The email stream association event triggers our custom plugin which add's user to google group. I am in process of moving that part to jive add-on.

 

Post request for user membership to group

 

var addMemberRequest = {
            'authz': "signed",
            url: 'jive_url/api/core/v3/members/places/"+groupid,
            method:'POST',
            'auth': env.auth,
            body:'{"person":"jive_url/api/core/v3/people/"+userid,","state" : "member"}'
        };

 

Post request for associating group with email stream

First we need to find the Email Watches stream for the user.

We get all stream of users using this get request and find the streamid for email watches

 

var getRequest = {
            'authz': "signed",
            url:'ive_url/api/core/v3/people'+userid+'streams'',
            method:'GET',
            'auth': env.auth
        };

 

And the the post request to associate group with stream

 

var associationRequest = {
            'authz': "signed",
            url: 'jive_url/api/core/v3/streams/'+streamId+'/associations',
            method:'POST',
            'auth': env.auth,
            body:'["' + jive_url/api/core/v3/places/'+placeId + '"]'
        };

At this  point right now our plugin gets triggered which adds user to corresponding google group.

This same plugin also takes care to adding/removing user from google group when that users follows/unfollows corresponding jive group in an email enabled stream.

We are in process of moving that to add-on.

 

Thats all what my extended version of Ryan Rutan's add-on does.

 

Going back to client side from where the request to my server is made.

We had to move this app from path "jive/settings/places/group" to "jive/actions/places/group" so that its easy to access for users.

Unfortunately there is no way to restrict app link to admins/super admins.

So I wrote some client side logic to achieve this.

I wrote some promises to get current groupID, current user's ID, group's admin and super admin.

 

I would like to share the simple jquery promises (I know Q is better) i wrote for this.

Here are the promises:

 

var jivePromises = {
    isSuperAdmin : function(){
        var adminPromise = $.Deferred();
        osapi.jive.corev3.securityGroups.get({"id": "1001"}).execute(function(data){
            if(data.status != '403' && data.type && data.type.toUpperCase() == 'SECURITYGROUP'){
                adminPromise.resolve(true);
            } else {
                adminPromise.reject(false);
            }
        });
        return adminPromise
    },
    getGroupAdmins: function(group){
        var groupAdminPromise = $.Deferred();
        group.getMembers({"state":"owner"}).execute(
            function (groupAdmins) {
                groupAdminPromise.resolve(groupAdmins);
            },
            function(error){
                groupAdminPromise.reject(error);
            }
        );
        return groupAdminPromise;
    },
    getGroup: function(groupId){
        var groupPromise = $.Deferred();
        osapi.jive.corev3.places.get({ entityDescriptor: 700 + "," + groupId }).execute(
            function(response) {
                groupPromise.resolve(response.list[0]);
            },
            function(error){
                groupPromise.reject(error);
            }
        );
        return groupPromise;
    },
   getCurrentUser:function(){
        var currentUserPromise = $.Deferred();
        osapi.people.getViewer().execute(
            function (currentUser) {
                currentUserPromise.resolve(currentUser.id);
            },
            function(error) {
                currentUserPromise.reject(error);
            }
        );
        return currentUserPromise;
    }
}

Thats all. One more nice example of what node.js based jive add-on can do.

Hi Jivers,

In jive 5 and jive 6, we had a custom java plugin called Moved Content which enabled us to move contents from one group / space to other in bulk.

As Jive 7 arrived, we decided to create add-on for the same feature to get rid of plugins and get advantage of the easy development and deployment that jive-sdk has.

We have this working on our production site.

 

The Add-on

This add-on is totally based on jive's javascript apis.

I have some other apps which use combination of javascript apis on website and REST api through a connected node service.

Thats how I have modified Ryan Rutan's admin essentials to control google group memberships and stream associations from jive.

I will share that soon..

 

How the app looks


Displaying the content in current place as per selected filter options and picker for target place at the top.

First is the blog posts view.

Internally in jive, blog posts are not linked directly to a group but to a place type called blog, which is then linked to the parent group.

So keeping two separate views to avoid complexity


Screen Shot 2015-01-09 at 1.47.31 PM.png


Showing all other supported content types on other pane


Screen Shot 2015-01-09 at 1.47.43 PM.png


Showing the progress post processing



Screen Shot 2015-01-09 at 2.37.38 PM.png


 

Our dev krunalpatel helped me design the UI.

 

Some Code

The code I have shared here is the simplest possible javascript code coz my intention is to help anyone with basic JS knowledge to understand and use this app and jive-sdk.

Trust me, with basic knowledge of javascript and jquery and jive's apis, you can create wonders.

You can use some promises and other jquery utils  to beautify the code below.

 

First the user is provided with place pickers to select source and target places.

 

var displaySourcePlacePicker = function(){
    osapi.jive.corev3.places.requestPicker({
        success : function(data) {
            $("#toGroup").val(data.name);
            $("#toGroup").attr("data-groupid", data.placeID);
        }
    });
}




Then user can select content types as shown in screenshot.

 

On either selection of source group or content types, I refresh the content by making a new api call to jive with content types.

I push every selected content type in array contentTypes to be sent to jive api.

And I send the groupID, contenttypes array and current index to the method below.


var displayContentInGroup = function (groupId, contentTypes, index){
    osapi.jive.corev3.contents.get({
        "place":opensocial.getEnvironment()['jiveUrl']+"/api/core/v3/places/"+groupId,
        "type":contentTypes.join(","),
        "startIndex":index.toString(),
        "count":itemsPerPage.toString()
    }).execute(function(data){
        $("#content-body").html("");
        var contentList = [];
        if(data.list.length != 0){
            contentList.push(createHtml(data, $("#selectAll:checked").val()));
            if (data.links && data.links.next)
                setupNextPaginationButton(index+itemsPerPage);
            if (data.links && data.links.previous)
                setupPrevPaginationButton(index-itemsPerPage);
        } else {
            contentList.push("<tr> \n<td colspan=4> <h3>No content to display</h3> </td> \n</tr>");
        }
        $("#content-body").append(contentList.join(""));
    });
};





After the user selects the contents and clicks Move Content button, I call this function.

 

var moveContent = function(targetGroupId){
    var contentIds = getIdsForSelectedContent();

        for (var i in contentIds){
            content_url = opensocial.getEnvironment()['jiveUrl']+"/api/core/v3/contents/"+contentIds[i]
            osapi.jive.corev3.contents.get({"uri":content_url}).execute(function(data){
                data.parent = opensocial.getEnvironment()['jiveUrl']+"/api/core/v3/places/"+$("#toGroup").attr("data-groupid");
                data.update().execute(function (data) {
                    if(i == (contentIds.length-1)){
                        displaySuccessMessage("Successfully Moved the content");
                        refreshContentList();
                    }
                });
            })
        }
};





The above api calls changes the parent place without changing the date of the content.

And no notification is shown in activity stream for this action, so its a silent move.

 

Please find the initial version of my move content app here --> Move-content-app by shahpawan

 

Special thanks to shilpak to help me with this and all the other jive add-ons we developed for Thoughtworks

 

Enjoy node.js and jive_sdk.

Node.js has awesome possibilities, and hence now Jive has awesome possibilities.

 

Note : For people on jive cloud version 8c4+, you can just download the extension.zip attached with this blog and upload it to jive instance.

No need to download the source code, no node server required to build it locally.

Filter Blog