Zimlet Classic Development
This skill should be used when the user asks about "classic zimlet", "zimlet XML", "zimlet handler", "zimlet panels", "zimlet dialogs", "zimlet context menu", "zimlet keyboard shortcut", "com_zimbra zimlet", or mentions developing zimlets for Zimbra Classic Web Client. Covers XML-based zimlet development.
What this skill does
# Zimlet Classic Development
Guide for developing zimlets for the Zimbra Classic Web Client using XML and JavaScript.
## Zimlet Architecture
Classic zimlets are XML-defined extensions with JavaScript handlers:
```
com_mycompany_myzimlet/
├── com_mycompany_myzimlet.xml # Zimlet definition
├── com_mycompany_myzimlet.js # JavaScript handler
├── com_mycompany_myzimlet.css # Styles (optional)
└── img/ # Images (optional)
└── icon.png
```
### Naming Convention
- Package name: `com_<company>_<zimletname>` (lowercase, underscores)
- All files must match package name
- Example: `com_acme_tickettracker`
## Zimlet XML Definition
### Basic Structure
```xml
<zimlet name="com_mycompany_myzimlet" version="1.0"
description="My Zimlet Description"
xmlns="urn:zimbraZimlet">
<!-- Panel item in side panel -->
<zimletPanelItem label="My Zimlet" icon="zimletIcon">
<toolTipText>Click to open My Zimlet</toolTipText>
</zimletPanelItem>
<!-- Include JavaScript handler -->
<include>com_mycompany_myzimlet.js</include>
<!-- Include CSS -->
<includeCSS>com_mycompany_myzimlet.css</includeCSS>
<!-- User properties (preferences) -->
<userProperties>
<property name="mySetting" type="string">default</property>
</userProperties>
</zimlet>
```
### Content Objects (Regex Matching)
Highlight and add actions to matched text:
```xml
<zimlet>
<contentObject>
<!-- Match ticket numbers like TICKET-1234 -->
<matchOn>
<regex attrs="ig">TICKET-(\d+)</regex>
</matchOn>
<!-- Actions for matched content -->
<onClick>
<actionUrl target="_blank" method="GET">
https://tickets.company.com/view/{$1}
</actionUrl>
</onClick>
<!-- Tooltip preview -->
<toolTip>
<contentUrl>
https://tickets.company.com/api/preview/{$1}
</contentUrl>
</toolTip>
</contentObject>
</zimlet>
```
### Context Menu Integration
```xml
<zimlet>
<!-- Add menu items to email context menu -->
<contextMenu>
<menuItem label="Create Ticket" id="CREATE_TICKET" icon="ticketIcon"/>
<menuItem label="Search Related" id="SEARCH_RELATED"/>
</contextMenu>
</zimlet>
```
## JavaScript Handler
### Basic Handler Structure
```javascript
/**
* Zimlet handler class
*/
function com_mycompany_myzimlet_HandlerObject() {
}
// Extend base zimlet class
com_mycompany_myzimlet_HandlerObject.prototype = new ZmZimletBase();
com_mycompany_myzimlet_HandlerObject.prototype.constructor =
com_mycompany_myzimlet_HandlerObject;
/**
* Called when zimlet is initialized
*/
com_mycompany_myzimlet_HandlerObject.prototype.init = function() {
// Load user properties
this._mySetting = this.getUserProperty("mySetting") || "default";
// Register listeners
this._registerListeners();
};
/**
* Called when panel item is single-clicked
*/
com_mycompany_myzimlet_HandlerObject.prototype.singleClicked = function() {
this._showDialog();
};
/**
* Called when panel item is double-clicked
*/
com_mycompany_myzimlet_HandlerObject.prototype.doubleClicked = function() {
this._openSettings();
};
```
### Dialogs
```javascript
/**
* Show a custom dialog
*/
com_mycompany_myzimlet_HandlerObject.prototype._showDialog = function() {
if (this._dialog) {
this._dialog.popup();
return;
}
var view = new DwtComposite(this.getShell());
view.setSize("400", "300");
view.getHtmlElement().innerHTML = this._createDialogContent();
this._dialog = new ZmDialog({
title: "My Zimlet",
view: view,
parent: this.getShell(),
standardButtons: [DwtDialog.OK_BUTTON, DwtDialog.CANCEL_BUTTON],
disposeOnPopDown: false
});
this._dialog.setButtonListener(DwtDialog.OK_BUTTON,
new AjxListener(this, this._onOkClick));
this._dialog.popup();
};
com_mycompany_myzimlet_HandlerObject.prototype._createDialogContent = function() {
return '<div class="myzimlet-container">' +
'<label>Enter value:</label>' +
'<input type="text" id="myzimlet-input" />' +
'</div>';
};
com_mycompany_myzimlet_HandlerObject.prototype._onOkClick = function() {
var input = document.getElementById("myzimlet-input");
var value = input ? input.value : "";
// Process value...
this._dialog.popdown();
};
```
### Context Menu Handling
```javascript
/**
* Called when context menu item is selected
*/
com_mycompany_myzimlet_HandlerObject.prototype.menuItemSelected =
function(itemId, item, ev) {
switch(itemId) {
case "CREATE_TICKET":
this._createTicket(item);
break;
case "SEARCH_RELATED":
this._searchRelated(item);
break;
}
};
com_mycompany_myzimlet_HandlerObject.prototype._createTicket = function(item) {
// Get email details
var msg = item;
var subject = msg.subject;
var from = msg.getAddress(AjxEmailAddress.FROM).toString();
// Open ticket creation URL
var url = "https://tickets.company.com/create?" +
"subject=" + encodeURIComponent(subject) +
"&from=" + encodeURIComponent(from);
window.open(url, "_blank");
};
```
### Making SOAP Requests
```javascript
/**
* Make SOAP request to Zimbra
*/
com_mycompany_myzimlet_HandlerObject.prototype._makeRequest = function() {
var soapDoc = AjxSoapDoc.create("GetInfoRequest", "urn:zimbraAccount");
var callback = new AjxCallback(this, this._handleResponse);
var errorCallback = new AjxCallback(this, this._handleError);
appCtxt.getAppController().sendRequest({
soapDoc: soapDoc,
asyncMode: true,
callback: callback,
errorCallback: errorCallback
});
};
com_mycompany_myzimlet_HandlerObject.prototype._handleResponse = function(result) {
var response = result.getResponse();
console.log("Response:", response);
};
com_mycompany_myzimlet_HandlerObject.prototype._handleError = function(error) {
this.displayErrorMessage("Request failed: " + error.msg);
};
```
### User Preferences
```javascript
/**
* Save user preference
*/
com_mycompany_myzimlet_HandlerObject.prototype._saveSetting = function(value) {
this.setUserProperty("mySetting", value, true); // true = save to server
};
/**
* Load user preference
*/
com_mycompany_myzimlet_HandlerObject.prototype._loadSetting = function() {
return this.getUserProperty("mySetting") || "default";
};
```
## Slots and Injection Points
Classic zimlets can inject into specific UI areas:
### Tab Integration
```xml
<zimlet>
<!-- Add as application tab -->
<zimletPanelItem label="My App" icon="myIcon">
<toolTipText>My Application</toolTipText>
</zimletPanelItem>
</zimlet>
```
```javascript
// Create tab application
com_mycompany_myzimlet_HandlerObject.prototype._createApp = function() {
var app = this.createApp("My App", "myIcon", "My Application");
return app;
};
```
### Toolbar Buttons
```javascript
// Add toolbar button
com_mycompany_myzimlet_HandlerObject.prototype._addToolbarButton = function() {
var toolbar = appCtxt.getCurrentView().getToolbar();
var button = new ZmToolBarButton({
parent: toolbar,
text: "My Action",
tooltip: "Perform my action"
});
button.addSelectionListener(new AjxListener(this, this._onToolbarClick));
};
```
## Deployment
### Package as ZIP
```bash
cd com_mycompany_myzimlet/
zip -r ../com_mycompany_myzimlet.zip *
```
### Deploy via zmzimletctl
```bash
# Deploy zimlet
zmzimletctl deploy com_mycompany_myzimlet.zip
# Enable for COS
zmzimletctl enable com_mycompany_myzimlet
# List installed zimlets
zmzimletctl listZimlets
# Undeploy
zmzimletctl undeploy com_mycompany_myzimlet
```
## DWT Widget Library (Discrete Widget Toolkit)
Classic zimlets use Zimbra's proprietary DWRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.