Changing IBM Connections behavior with IBM Connections Customizer
Using extensions to support new business processes
IBM Connections Customizer lets you change both the look and feel and the behavior of IBM Connections. Changing the look and feel gives your users an interface that complies with corporate design standards or provides easier ways to access information. This article, the first in a two-part series, focuses on how you can use Customizer to change Connections behavior to fit your business needs.
This article series describes a project I recently completed that implements the following behaviors:
- A new “Guest Model” for IBM Connections Cloud. By default, IBM Connections Cloud allows any user to invite guests. You can also prevent everyone from inviting guests, but you cannot grant specific people the right to invite guests while preventing others. The Customizer Guest Model extension gives you that ability, so you can centrally control access to content by people external to your organization. Building that capability is the focus of this first article in the series.
- Privacy rules for routine actions. When someone previews or downloads an IBM Connections file, the download counter is incremented. Anyone with read access to the file can see who viewed or downloaded each version, and when. However, a user may have clicked the file or download link accidentally; and in any case, viewing or downloading a file does not mean that the user read and mastered its content. Also, in certain countries, showing this information to others may conflict with privacy regulations. Customizer let me provide a way to selectively hide download and preview information for certain users; for example, people from a given country or with a given title, or people selected with some other rule. This will be the focus of the second article in the series.
Clearly, for this project I needed to manage several things at the same time. Because of the complexity of the different targets and because I wanted to make my solution modular, I decided to create several Customizer scripts, each implementing discrete functionality. I had to answer a couple of questions to do so.
- The complexity of the page and user interactions meant that several scripts could be running at the same time. How could I make sure that the scripts didn’t overlap, and also avoid “heavy” operations like XML HTTP requests?
This article shows how I answered those questions and designed and implemented my solution.
Note: The code associated with this article is publicly available in the OpenCode4Connections Github repository:
I recommend that you clone the Github repository now so that you can refer to it as you read.
“Guest Model” feature scope
The scope associated with inviting “Guests” is not limited simply to hiding or showing the “Invite Guests” item in the Connections User menu. There are many other ways to invite guests.
- Allowing someone to create a Community open to external access.
- Allowing someone to add a File or Folder that can be shared with external people.
- Allowing someone to create an Activity that can be made available for contributions by external people.
- Allowing someone who owns an externally open community to add members that are not internal users or previously invited guests.
I had to properly manage all these situations to prevent mistakes.
Managing an Access Control List
The IBM Connections Customizer scripts I created needed to apply conditionally, based on the identity of the logged in user. The “match” clause in an extension definition lets you list the names of the people that the script will affect.
For a big organization, this list could become quite long, so managing it in the extension definition document would become problematic. It’s also error-prone because of the syntax requirements.
We need to find a friendlier and more agile mechanism to manage the access control list (ACL).
I decided to use the membership of a specially-created IBM Connections Community, whose only reason to exist is to define the membership, as my ACL. This makes it very easy to modify the access control list, simply by using the standard IBM Connections interface to add and remove community members, with no additional housekeeping. Members of the community would be allowed to invite guests, while everyone else would not.
Guest Model extension configuration
The “Guest Model” behavior is packaged as a single application comprising six extensions and seven scripts.
|AA – Common Functions||commonTools.js|
|CommunityNoExternal||CommunityNoExternal.js||communities||Manages the creation of restricted, open-to-external-people communities|
|CommunityNoMembers||CommunityNoMembers.js||communities||Manages adding and inviting members in restricted, open-to-external communities|
|ActivitiesNoExternal||ActivityNoExternal.js||activities||Manages the creation of activities that are open to external people|
|FilesNoExternal||FilesNoExternal.js||files||Manages the creation of files and folders|
|AddInviteGuest||AddInviteGuest.js||global||Ensures that users with the correct access can see the “Invite Guest” menu item|
For consistency reasons, these extensions are packed in a single application JSON file (GuestModel_Common.json). You simply load this file into your organization's application registry and all the required behaviors will be added.
Notice that the ”AA – Common Functions” extension has a special prefix. This extension implements some common functions and global variables used by the other extensions. Since extensions are loaded alphabetically, the “AA –“ prefix forces the IBM Connections Customizer engine to inject the relevant scripts (commonTools.js and GuestModel_common.js) before any other extension using them; in this way, all the other scripts can safely rely on the fact that the two common scripts are loaded and executed before they need them. In addition, the two common scripts are only loaded once and are loaded for all the IBM Connections pages (“global” path) that are affected by the IBM Connections Customizer injector.
The important features implemented by those two scripts are described later in this article. For now, it is important only to highlight that:
- commonTools.js contains functions and classes used across the two packages described in this article series, “Guest Model” and “Privacy Rules”.
- GuestModel_common.js contains functions and classes specific to the “Guest Model” package.
This script controls the behavior associated with the creation of a new restricted community.
- If the community creator is allowed to invite guests, the only change is that the option to make the restricted community open to external users is unchecked by default, but available for selection.
- If the community creator is not allowed to invite guests, the option to make the restricted community open to external users is not available, and the created community is not open to external users.
This script is restricted only to "communities/service/html/communitycreate" pages within the "communities" service.
Lesson Learned: The function __cbill_uncheckBox in commonTools.js changes the default “checked” state of the checkbox. It triggers a mouse click event on the checkbox to make sure that any further cascading behavior associated with a mouse click occurs. Simply changing the visual state of the checkbox might cause required processing to be omitted, leading to inconsistencies between the user interface and the state of Connections.
This script controls the behavior associated with adding or inviting new members to an existing restricted community that is open to external users. The script uses a different access control list than the other scripts, to provide additional granularity by separating the ability to add and invite guest users from the ability to create open-to-external communities. In other words, a user may be given the ability to create a community open to external users, but not the right to invite external users. The converse is also true. A user without the ability create a community open to external users can still be added to one as an owner, and be granted the right to invite external users.
- The script waits for the creation of the input textbox widget where
the user enters the name of the person to be added or invited to
the Community. This widget belongs to the CSS class “dijitReset
dijitInputField dijitInputContainer typeaheadInput”. The script waits
for an instance of a widget with that class to appear on the
When the textbox is displayed, the script hides the “+” button to its right, thus preventing the user from adding people that are not part of the organization directory:
Lesson Learned: Since native actions may return visibility to the “+” button, I implemented an “observer” that resets the visibility of the button to “hidden” any time it changes.
- The script also addresses a special case. Suppose an owner of a
restricted, open-to-external community, who is not allowed to invite
guests, has an external person in his Contacts. When trying to add a
member to the community, the owner does not have access to the “+”
button that allows him to invite external people as guests. However,
if he starts typing the name of the person in his Contacts list, he
will be able to add that external contact as a guest.
To avoid this, the script implements a listener which is activated when the form to invite the person is created (this form belongs to the “dijitDialogPaneContent” CSS class). When the form appears, the script hides the “Submit” button and adds a link to a custom page where the organization describes the formal process to be followed to invite an external guest (probably a page where an internal contact person is shown who has the rights to invite guests).
- The URL of the custom page is defined as the value of the variable __GuestModel_serviceDeskURL which, itself, is defined at the end of GuestModel_common.js.
Note that this script only applies to the "communities" service. It cannot be further restricted as the page that will require the script may not provide a clearly identifiable URL.
This script controls the behavior associated with the creation of a new activity.
- If the activity creator is allowed to invite guests, the only change is that, by default, the option to make the activity open to external users is available but unchecked.
- If the activity creator is not allowed to invite guests, the option to make the activity open to external users is not available, and the created activity is therefore not open to external users.
The behavior is very similar to the one described for the CommunityNoExternal script. The only notable difference is that the script creates a Listener on the “Start Activity” button. This listener implements the logic on the new form that appears when the user clicks the button.
Lesson Learned: This script addresses an important point. The creation of a new activity is performed by following the URL https://<cloud-domain>/activities/service/html/mainpage#dashboard,myactivities (notice the “hash qualifier,” #dashboard,myactivities). Connections does not necessarily load the hash-qualified page from the server; it may be loaded from the cache without retrieving new code from the server. This means that we cannot bind the execution of the script to the loading of such hash-qualified URLs. Instead, the script assumes it will operate on any activity page and defines a listener on the “hashchange” event happening on “any activity page.” When the “hashchange” event occurs, the script evaluates whether the new page points to the “#dashboard,myactivities” qualified URL. If so, it triggers the behavior of adding a listener to the “Start Activity” button.
This script controls the behavior associated with the creation or upload of a new file and with the creation of a new Folder.
- If the user is allowed to invite guests, the only change is that, by default, the option to make the file or folder accessible to external users is available but unchecked.
- If the user is not allowed to invite guests, the option to make the file or folder accessible to external users is not available. The file or folder will be created or uploaded without being open to external users.
This script applies to the "files" service .
Note that this script needs to take into consideration the following use cases.
File or folder creation from menu
The user uploads a file, creates a folder, or creates a document, spreadsheet, or presentation. The strategy used for each situation is similar.
- The script waits for the “New” button to be made available on the page. (This button is identified by a unique Id, “lconn_files_action_createitem_0”.) Then it adds a listener for the “click” event on that button.
- Once the user clicks the “New” button, the script waits until the “Upload…” item is created in the dropdown. (When this is created, the assumption is that all the others will be also created.)
- A listener is added for the “click” event on each of the five items in the dropdown.
- When any of the items is clicked, the script waits for the creation of the appropriate checkbox (identified by [name="_setExt"]) in the form that allows the upload or creation. For example:
- Finally, the script applies the logic to change the “default” status of the checkbox to “unchecked,” and to hide the whole “External Access” line if the user is not authorized to invite external users.
Lesson Learned: Each time the user uploads a file or creates it, a new form is created; previously used forms are not removed. This means that there may be multiple instances of checkboxes identified by [name="_setExt"]. This is why the script retrieves the checkbox whose parent form is actually visible.
File creation by drag and drop
The user drags a file from the desktop to the browser page.
In this case, a listener is added to the area of the screen in which the user could drop a file (identified by the id “lotusFrame”); the listener is activated by the “drop” event. From that point, the behavior is similar to the one previously described for file and folder creation using the dropdown.
This script controls the behavior associated with the “Invite Guest” item in the user’s dropdown in the menu bar.
Ideally this would be a simple script.
- If the user is allowed to invite guests, nothing will change in the dropdown.
- If the user is not allowed to invite guests, the “Invite Guest” item in the dropdown will be removed.
Unfortunately, things are a little more complex than this. IBM Connections Customizer does not operate on all the pages on which the Menu bar is shown. Specifically, it does not operate in Verse, the Calendar, the “administration” page, or on any of the “report” pages. This means that an unauthorized user who loads one of those pages would see the “Invite Guest” item in the dropdown because Customizer was not able to prevent it. Although this occurs only on those pages, that’s enough to break the security context that the package establishes.
For this reason, I used a different strategy.
- Menu bar extension points allow the organization administrator to
modify the menu bar consistently across all pages. I decided to use
these extension points to remove the “Invite Guest” option from all pages and then use Customizer to add it back wherever possible, when appropriate. This is
what the “RemoveInviteGuest.json” extension provides.
AddInviteGuest.js then re-defines the “Invite Guest” option in the dropdown for the people who have the proper rights.
- The behavior of the AddInviteGuest.js script is simple.
- The script waits for the dropdown to be created.
- Once it is created, it adds a “custom version” of the “Invite Guest” option in the right place in the dropdown.
There is a small drawback to this strategy: the “Invite Guest” option will not appear in the dropdown when authorized users visit pages from Verse, the Calendar, or the Administration or reporting pages. However, this is probably not important, since those pages are not part of Connections.
This file contains functions and classes used by all the packages. There are a few things to note.
This convenience class waits for the right version of Dojo to be loaded on the page. Most of the previously described scripts assume that Dojo is loaded, so each script creates an instance of this class and then executes the “do” method on the instance.
The “do” method checks for Dojo a set number of times and returns once Dojo is loaded. It then executes a user defined callback, which constitutes the majority of the code in each script.
When an instance of this class is created, it accepts a “label” attribute value. This is used for debugging and keeps track of the context in which the instance is invoked.
__cBill_waitById and __cBill_waitByQuery
These convenience classes wait for one or more widgets (identified by id or CSS query expression) to be available on the page. __cBill_waitByQuery returns an array of widgets, while __cBill_waitById returns a single widget. A script will create an instance of the appropriate class and then execute the “do” method on the instance.
The “do” method checks for the widget(s) and returns once the widgets are found. It then executes a user defined callback which receives, as input, the handle to the retrieved widget(s) for the callback to operate on.
When an instance of either class is created, it accepts a “label” attribute value. This is used for debugging and keeps track of the context in which the instance is invoked. Both classes support the following attributes.
- onlyWhenVisible: If true, a widget is returned only if its offsetHeight property value is greater than 0.
- onlyWhenParentVisible and parentToBeVisible: If onlyWhenParentVisible is true, a widget is returned only if the parent named by parentToBeVisible is visible.
__cBill_logger and __cBill_alert
These functions dump debug messages if the __cBill_debug global variable is set to true. __cBill_logger dumps on the console while __cBill_alert provides a popup.
This global variable is the URL of the IBM Connections endpoint. It is used to build the URL for XHR calls.
This file contains functions and classes that are used by the GuestModel package only. Again, there are a few things to note.
This class defines the behavior associated with the ACL community. The class needs to be instantiated by providing a “uuid” parameter with the “new” keyword. This is the UUID of the ACL community.
The main method that you should invoke after instantiation is the checkUser method. This method verifies that the current user is a member of the relevant community and returns a user defined callback which receives, as input, a “true/false” value if the user is member.
The IBM Connections APIs provide an easy way to check if a person is a member of the ACL community, via the endpoint ”/communities/service/json/v1/community/activepersonmembers”. The Customizer script issues an XMLHttpRequest against the endpoint. The script runs from the same domain of the Connections page the user is executing and thus automatically inherits the credentials of the currently logged in user.
The owner of the ACL community would probably be the organization administrator (because the organization administrator is also the one who is allowed to declare or modify the IBM Connections Customizer extensions for the organization).
This community must be restricted or private.
- If it is restricted, the XMLHttpRequest call to retrieve the current members of the Community will return a list of users to be parsed. The current user’s UUID is stored in the “ids” cookie.
- If it is private and the current user is not a member of the community, the API will return a 403 error, meaning that the current user is not allowed to access the community. Therefore, using a private community lets you avoid parsing the list of members.
You can create as many instances of __GuestModel_UserAllowed as you wish, to create different types of ACLs. The GuestModel package only uses two instances of this class, named __GuestModel_firstACL and __GuestModel_secondACL.
- __GuestModel_firstACL manages the ACL associated with the rights to create IBM Connections artifacts that could lead to opening the organization to external people.
- __GuestModel_secondACL manages the ACL associated with inherited behavior. (Someone is the owner of a restricted, open to external community without having herself created it. See CommunityNoMembers.js).
Since an XMLHttpRequest is a “heavy” operation, the code implements a couple of concepts to minimize its use.
- The first time a script checks for the ACL, the result is stored in a
session cookie. This cookie is checked before attempting to issue the
XMLHttpRequest. The cookie naming convention is
“Customizer-” <userUUID> “-“ <communityUUID>
The only drawback is that a user must close his browser in order to inherit from a change in the ACL. I thought that the runtime advantage would compensate for this small limitation, which needs to be known anyway by the organization administrator.
- The checkUser method may be invoked by many scripts. In addition to checking the cookie, the method also does not execute the XMLHttpRequest if some other part of the code has already started to issue an XMLHttpRequest.
This global variable is used to provide a clickable URL for the users who will be shown directions on how the organization implements the GuestModel policy.
In this article I showed how IBM Connections Customizer can help you modify IBM Connections Cloud behavior. This goes well beyond simply changing the user interface, although that was part of the work, as a result of the application of a new business rule. In the second article of this series, I will show how I was able to selectively hide the identity of people who perform certain actions.
As these examples show, the possibilities are endless:
- You could implement IBM Connections Customizer scripts that simplify tasks for your end users.
- You could inject Watson's intelligence into interactions to enrich them and, especially, to enrich the final content managed by IBM Connections.
- You could provide on-the-glass integration with external sources of information to better contextualize the activities performed by the user on a given page.
In this article, I also shared with you some things I learned during the development of my scripts.
- Exploiting Connections itself as a data source for Customizer has been an interesting learning experience.
I encourage you to take your learning further with the educational material listed in the "Related topics" section below. I also invite you to clone or fork the Github repository (https://github.com/OpenCode4Connections/customizer-guest-model) associated with the project described here, and help me improve the quality and reach of the code.
- IBM Connections Customizer documentation
- Taking control of the IBM Connections user experience: Introducing IBM Connections Customizer
- IBM Connections Customizer is available! What's New?
- IBM Connections Customizer Tutorial, Part 1
- IBM Connections Customizer Tutorial, Part 2
- IBM Connections Customizer Tutorial, Part 3
- IBM Connections Customizer Tutorial, Part 4
- IBM Connections Customizer Tutorial, Part 5