note: A license is required to enable the Voice tab for Unified Communications. Additionally the tab is controlled by setting zimbraFeatureVoiceEnabled to TRUE at the COS or user level.

Voice Mail Tab

The voice mail tab provides a list view of a user’s voice mails. Depending on the provider implementation it may also consist of missed calls, answered calls and placed calls.

The Voice Mail tab is populated by making a call to SearchVoiceRequest. More details can be found in soap-voice.txt.

Zimbra Voice Extension

We assume the reader has already installed ZimbraServer and knows how to restart the jetty server. If not please refer to ZimbraServer/docs/INSTALL-*.txt.

Directories needed from perforce

  • ZimbraVoice/*
  • ZimbraCustomerServices/*
  • Telephony/*

Code for new ZimbraVoice extension should go under Telephony/ directory. Examples: Telephony/Cisco, Telephony/Mitel. Mitel & Cisco are existing voice implementations under Telephony directory. Please refer to them for any info or help. ZimbraVoice extension --> "Telephony/{ZimbraVoice-Extension}" Directory structure should follow

  • Telephony/{ZimbraVoice-Extension}/ZimbraServer (Example: Telephony/Cisco/ZimbraServer)
  • Telephony/{ZimbraVoice-Extension}/docs/* (Example: Telephony/Cisco/docs)
  • Telephony/{ZimbraVoice-Extension}/build.xml (Example: Telephony/Cisco/build.xml)
  • Telephony/{ZimbraVoice-Extension}/ZimbraServer directory structure should follow:
  • Telephony/{ZimbraVoice-Extension}ZimbraServer/src/*
  • Telephony/{ZimbraVoice-Extension}ZimbraServer/conf/*
  • Telephony/{ZimbraVoice-Extension}ZimbraServer/build.xml
  • Telephony/{ZimbraVoice-Extension}/build.xml & Telephony/{ZimbraVoice-Extension}/ZimbraServer/build.xml can be copied from existing voice implementation and edited appropriately.

ZimbraVoice/src/java/com/zimbra/cs/voice/VoiceStore.java is the voice server extension. The voice store provider extension has to be a subclass of VoiceStore.java. The new voice store extension acts as a middle man between ZimbraWebClient and the Voice Server. Please check the existing voice store modules (CiscoVoiceStore.java, MitelVoiceStore.java) for more info on implementation.

ZimbraVoice/docs/soap-voice.txt details the available Voice SOAP APIs. All SOAP requests are directed to appropriate handler classes based on the element to class mapping in VoiceService.java and the classes take care of calling methods from voice store provider extension (subclass of VoiceStore.java)

How to add a new Voice Soap API?

To add new SOAP API, a Request & Response element is added to VoiceConstants.java and the request element is registered in VoiceService.java by mapping to a handler class. If the mapped class needs to call any new function, the function is added to VoiceStore.java and implemented in all other existing voice extensions. Response element is used to generate SOAP responses. Please refer to any existing voice module for more information regarding the new API implementation.

Usually the new API changes may impact other existing voice implementations, so its recommended to avoid making those changes as much as possible. The new API needs to be tested using "zmsoap" and ZimbraVoice/docs/soap-voice.txt needs to be updated.

External users or 3rd part audience can only implement code in their own voice store extensions but they don't have the ability to make changes to ZimbraVoice (VoiceStore.java) or add new APIs, although they can suggest changes to the internal code or implementations.

ZimbraVoice is an extension deployed under /opt/zimbra/lib/ext/voice and the voice store provider is also a server extension deployed under /opt/zimbra/lib/ext/{ZimbraVoice-extension}

Make sure to restart jetty server once the code is built and deployed in /opt/zimbra/lib/ext/ directory. Server uses ClassLoader to load the instance of VoiceStore.java as parent class which in-turn uses URLClassLoader to load the voice store provider class.

Click to Call

Providing click to call capability via a zimlet can be broken down into three areas.

Contact Card

Adding Click to Call icon to the contact card In order to add the click to call icon to the contact card you need to first register your zimlet as a subscriber:

function(emailZimlet) {
 MyClick2CallZimlet.prototype.onEmailHoverOver =
 function(emailZimlet) {

You can then add an icon to the contact card and register a callback for implementing functionality by adding a EmailToolTipSlide:

 MyClick2CallZimlet.prototype._addSlide =
 function(emailZimlet) {
    //Do not show "Call" slide if unauthorized to use UC feature
    if (!appCtxt.getSettings()._hasVoiceFeature()) {
    var tthtml = this._getTooltipBGHtml(); //HTML
    var selectCallback =  new AjxCallback(this, this._handleSlideSelect);
    this._slide = new EmailToolTipSlide(tthtml, true, "Click2CallZimletIcon", selectCallback, "Click to call");

Finally, your zimlet needs to implement onPhoneClicked:

 MyClick2CallZimlet.prototype.onPhoneClicked =
 function(phone) {
    if (!this.myFromPhoneDlg) {
        this.myFromPhoneDlg = new MyClick2CallFromPhoneDlg(this.getShell(), this,
    this.MyFromPhoneDlg.toPhoneNumber = this.toPhoneNumber = phone;

Providing a click to call dialog

Now that you have your zimlet registered with the contact card, you’ll want to implement a dialog to display when the user clicks on your icon to make a call.

To create the dialog you can extend ZmDialog:

 MyClick2CallFromPhoneDlg = function(shell, parent) {
  this.zimlet = parent;
  this.toPhoneNumber = "";
  this._dialogView = new DwtComposite(appCtxt.getShell());
  this._dialogView.setSize(300, 125);
  DwtDialog.call(this, {
    parent: shell,
    className: "ZmClick2CallFromPhoneDlg",
    title: this.zimlet.getMessage("fromPhoneDlgTitle"),
    view: this._dialogView,
    standardButtons: [DwtDialog.NO_BUTTONS],
    mode: DwtBaseDialog.MODELESS
  this._buttonDesc = {};
  this._isLoaded = false;
  this.RE = new RegExp("\\+?\\b\\d([0-9\\(\\)\\.\\s\\-]){8,20}\\d\\b");
 MyClick2CallFromPhoneDlg.prototype = new ZmDialog; 
 MyClick2CallFromPhoneDlg.prototype.constructor = MyClick2CallFromPhoneDlg;

In order to use this dialog to make calls you’ll need to know the from & to. In cases like the contact card you’ll want to prefill the “to” number. You can do so with something like:

  var cardAttributes = this.emailZimlet && this.emailZimlet.contactCard && this.emailZimlet.contactCard.attribs;
 if (!cardAttributes) return;
 this.click2CallDlg.toPhoneNumber = this.zimlet.toPhoneNumber = cardAttributes.mobilePhone || cardAttributes.workPhone;

In order to get the “from” number(s) (e.g. show a drop down of the numbers available to the user), the request will be based on your UC provider. Here’s one example of issuing a GetVoiceInfoRequest to retrieve the phone numbers:

 MyClick2CallFromPhoneDlg.prototype._getVoiceInfoAndShowDlg =
 function() {
 var soapDoc = AjxSoapDoc.create("GetVoiceInfoRequest", "urn:zimbraVoice");
 var respCallback = new AjxCallback(this, this._handleResponseVoiceInfo);
 var respErrorCallback = new AjxCallback(this, this._handleErrorResponseVoiceInfo);
 var params = {
    soapDoc: soapDoc,
    asyncMode: true,
    noBusyOverlay: true,
    callback: respCallback,
    errorCallback: respErrorCallback
 this._gettingVoiceInfo = true;

In this case the callback would parse the response for GetVoiceInfoResponse.phones.

Registering the click to call dialog with phone objects

In order to get your click to call dialog to be invoked when an email or contact has a phone number your zimlet needs to implement the following methods: 

 MyClick2CallZimlet.prototype.match = function(line, startIndex) {
  var re = this.RE;
  re.lastIndex = startIndex;
  var m = re.exec(line);
  if (!m) { return m; }
  var phone = m[0]
  if (phone.length > 10 &&
   phone[0] != "+"   &&
   !(AjxUtil.arrayContains(phone, " ")) &&
   !(AjxUtil.arrayContains(phone, ".")) &&
   !(AjxUtil.arrayContains(phone, "-"))) {
     return null;
    else {
     m[0] = phone;
     return m;

Then handle your matches by implementing the clicked method:

MyClick2CallZimlet.prototype.clicked = function(myElement, toPhoneNumber) { 
 this.toPhoneNumber = toPhoneNumber; this._showFromPhoneDlg(); 

Your zimlet XML configuration should have something like:

<contentObject type="phone">
			<regex attrs="g">\+?\(?\b\d([0-9\(\)\.\s\-]){8,20}\d\b</regex>

Presence Integration

In Z8 presence information is integrated into the “Email Contact Details” zimlet and displayed on the contact card when a user hovers over an email address. In order to add presence to the email zimlet, you need to register your zimlet that is providing presence as a subscriber. The email zimlet will notify other zimlets that implement “onEmailHoverOver”. An example of setting up presence for your zimlet:

  MyPresenceZimlet.prototype.onEmailHoverOver = function(emailZimlet) { 
   emailZimlet.addSubscriberZimlet(this, false, {presenceCallback: this.getPresence.bind(this)}); 

Your getPresence method then may make a REST call to retrieve the presence based on the email address which is being hovered. The presenceCallback function will be registered by the email zimlet and handle presence calls and caching.

Jump to: navigation, search