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
- 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.
- From a coding perspective, I wanted to reuse as much common code as I
are the common artifacts that would benefit all the scripts?
- 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
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
- 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
|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
|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
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
- 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
- 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
This script is restricted only to
“communities/service/html/communitycreate” pages within the “communities”
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
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
- 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
- The URL of the custom page is defined as the value of the variable
__GuestModel_serviceDeskURL which, itself, is defined at the end of
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
- 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
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
This script applies to the “files” service .
Note that this script needs to take into consideration the following use
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
- The script waits for the “New” button to be made available on the
(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
- 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
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
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
- 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.
to the “global” path so it can be executed on all possible pages supported
by IBM Connections Customizer. There are several URLs, though, that do
not load Dojo the way we expect. This is why the script escapes those URLs
at its beginning.
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
- 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
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.
Since GuestModel_common.js may be loaded multiple times for any single
page and the order may be affected by the single threaded architecture of
__GuestModel_UserAllowed class are created within the GuestModel_common.js
script. The script ensures that instances are not duplicated.
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_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
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> “-“
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
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
As these examples show, the possibilities
- 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
- Exploiting the asynchronous and single-threaded architecture of
rewarding because the user will not feel any performance degradation
caused by the IBM
- Exploiting Connections
itself as a data source for Customizer has been an interesting learning
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
associated with the project described here, and help me improve the
quality and reach of the code.