diff options
Diffstat (limited to 'mailnews/addrbook/prefs/content')
4 files changed, 731 insertions, 0 deletions
diff --git a/mailnews/addrbook/prefs/content/pref-directory-add.js b/mailnews/addrbook/prefs/content/pref-directory-add.js new file mode 100644 index 000000000..011b8aed9 --- /dev/null +++ b/mailnews/addrbook/prefs/content/pref-directory-add.js @@ -0,0 +1,394 @@ +/* -*- Mode: Java; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource:///modules/mailServices.js"); +Components.utils.import("resource:///modules/hostnameUtils.jsm"); + +var gCurrentDirectory = null; +var gReplicationBundle = null; +var gReplicationService = + Components.classes["@mozilla.org/addressbook/ldap-replication-service;1"]. + getService(Components.interfaces.nsIAbLDAPReplicationService); +var gReplicationCancelled = false; +var gProgressText; +var gProgressMeter; +var gDownloadInProgress = false; + +var kDefaultMaxHits = 100; +var kDefaultLDAPPort = 389; +var kDefaultSecureLDAPPort = 636; +var kLDAPDirectory = 0; // defined in nsDirPrefs.h + +var ldapOfflineObserver = { + observe: function(subject, topic, state) + { + // sanity checks + if (topic != "network:offline-status-changed") return; + setDownloadOfflineOnlineState(state == "offline"); + } +} + +function Startup() +{ + gReplicationBundle = document.getElementById("bundle_replication"); + + document.getElementById("download").label = + gReplicationBundle.getString("downloadButton"); + document.getElementById("download").accessKey = + gReplicationBundle.getString("downloadButton.accesskey"); + + if ("arguments" in window && window.arguments[0]) { + gCurrentDirectory = window.arguments[0].selectedDirectory; + try { + fillSettings(); + } catch (ex) { + dump("pref-directory-add.js:Startup(): fillSettings() exception: " + + ex + "\n"); + } + + let oldListName = gCurrentDirectory.dirName; + document.title = gReplicationBundle.getFormattedString("directoryTitleEdit", [oldListName]); + + // Only set up the download button for online/offline status toggling + // if the pref isn't locked to disable the button. + if (!Services.prefs.prefIsLocked(gCurrentDirectory.dirPrefId + + ".disable_button_download")) { + // Now connect to the offline/online observer + Services.obs.addObserver(ldapOfflineObserver, + "network:offline-status-changed", false); + + // Now set the initial offline/online state and update the state + setDownloadOfflineOnlineState(Services.io.offline); + } + } else { + document.title = gReplicationBundle.getString("directoryTitleNew"); + fillDefaultSettings(); + // Don't add observer here as it doesn't make any sense. + } +} + +function onUnload() +{ + if ("arguments" in window && + window.arguments[0] && + !Services.prefs.prefIsLocked(gCurrentDirectory.dirPrefId + + ".disable_button_download")) { + // Remove the observer that we put in on dialog startup + Services.obs.removeObserver(ldapOfflineObserver, + "network:offline-status-changed"); + } +} + +var progressListener = { + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) + { + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_START) { + // start the spinning + gProgressMeter.setAttribute("mode", "undetermined"); + gProgressText.value = gReplicationBundle.getString(aStatus ? + "replicationStarted" : + "changesStarted"); + gDownloadInProgress = true; + document.getElementById("download").label = + gReplicationBundle.getString("cancelDownloadButton"); + document.getElementById("download").accessKey = + gReplicationBundle.getString("cancelDownloadButton.accesskey"); + } + + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) { + EndDownload(aStatus); + } + }, + onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) + { + gProgressText.value = gReplicationBundle.getFormattedString("currentCount", + [aCurSelfProgress]); + }, + onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) + { + }, + onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) + { + }, + onSecurityChange: function(aWebProgress, aRequest, state) + { + }, + QueryInterface : function(iid) + { + if (iid.equals(Components.interfaces.nsIWebProgressListener) || + iid.equals(Components.interfaces.nsISupportsWeakReference) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + } +}; + +function DownloadNow() +{ + if (!gDownloadInProgress) { + gProgressText = document.getElementById("replicationProgressText"); + gProgressMeter = document.getElementById("replicationProgressMeter"); + + gProgressText.hidden = false; + gProgressMeter.hidden = false; + gReplicationCancelled = false; + + try { + if (gCurrentDirectory instanceof Components.interfaces.nsIAbLDAPDirectory) + gReplicationService.startReplication(gCurrentDirectory, + progressListener); + else + EndDownload(false); + } + catch (ex) { + EndDownload(false); + } + } else { + gReplicationCancelled = true; + try { + gReplicationService.cancelReplication(gCurrentDirectory.dirPrefId); + } + catch (ex) { + // XXX todo + // perhaps replication hasn't started yet? This can happen if you hit cancel after attempting to replication when offline + dump("unexpected failure while cancelling. ex=" + ex + "\n"); + } + } +} + +function EndDownload(aStatus) +{ + document.getElementById("download").label = + gReplicationBundle.getString("downloadButton"); + document.getElementById("download").accessKey = + gReplicationBundle.getString("downloadButton.accesskey"); + + // stop the spinning + gProgressMeter.setAttribute("mode", "normal"); + gProgressMeter.setAttribute("value", "100"); + gProgressMeter.hidden = true; + + gDownloadInProgress = false; + gProgressText.value = + gReplicationBundle.getString(aStatus ? "replicationSucceeded" : + gReplicationCancelled ? "replicationCancelled" : + "replicationFailed"); +} + +// fill the settings panel with the data from the preferences. +// +function fillSettings() +{ + document.getElementById("description").value = gCurrentDirectory.dirName; + + if (gCurrentDirectory instanceof Components.interfaces.nsIAbLDAPDirectory) { + var ldapUrl = gCurrentDirectory.lDAPURL; + + document.getElementById("results").value = gCurrentDirectory.maxHits; + document.getElementById("login").value = gCurrentDirectory.authDn; + document.getElementById("hostname").value = ldapUrl.host; + document.getElementById("basedn").value = ldapUrl.dn; + document.getElementById("search").value = ldapUrl.filter; + + var sub = document.getElementById("sub"); + switch(ldapUrl.scope) { + case Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL: + sub.radioGroup.selectedItem = document.getElementById("one"); + break; + default: + sub.radioGroup.selectedItem = sub; + break; + } + + var sasl = document.getElementById("saslMechanism"); + switch (gCurrentDirectory.saslMechanism) { + case "GSSAPI": + sasl.selectedItem = document.getElementById("GSSAPI"); + break; + default: + sasl.selectedItem = document.getElementById("Simple"); + break; + } + + var secure = ldapUrl.options & ldapUrl.OPT_SECURE + if (secure) + document.getElementById("secure").setAttribute("checked", "true"); + + if (ldapUrl.port == -1) + document.getElementById("port").value = + (secure ? kDefaultSecureLDAPPort : kDefaultLDAPPort); + else + document.getElementById("port").value = ldapUrl.port; + } + + // check if any of the preferences for this server are locked. + //If they are locked disable them + DisableUriFields(gCurrentDirectory.dirPrefId + ".uri"); + DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".description", "description"); + DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".disable_button_download", "download"); + DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".maxHits", "results"); + DisableElementIfPrefIsLocked(gCurrentDirectory.dirPrefId + ".auth.dn", "login"); +} + +function DisableElementIfPrefIsLocked(aPrefName, aElementId) +{ + if (Services.prefs.prefIsLocked(aPrefName)) + document.getElementById(aElementId).setAttribute('disabled', true); +} + +// disables all the text fields corresponding to the .uri pref. +function DisableUriFields(aPrefName) +{ + if (Services.prefs.prefIsLocked(aPrefName)) { + let lockedElements = document.querySelectorAll('[disableiflocked="true"]'); + for (let i = 0; i < lockedElements.length; i++) + lockedElements[i].setAttribute('disabled', 'true'); + } +} + +function onSecure() +{ + document.getElementById("port").value = + document.getElementById("secure").checked ? kDefaultSecureLDAPPort : + kDefaultLDAPPort; +} + +function fillDefaultSettings() +{ + document.getElementById("port").value = kDefaultLDAPPort; + document.getElementById("results").value = kDefaultMaxHits; + var sub = document.getElementById("sub"); + sub.radioGroup.selectedItem = sub; + + // Disable the download button and add some text indicating why. + document.getElementById("download").disabled = true; + document.getElementById("downloadWarningMsg").hidden = false; + document.getElementById("downloadWarningMsg").textContent = document. + getElementById("bundle_addressBook"). + getString("abReplicationSaveSettings"); +} + +function hasCharacters(number) +{ + var re = /[0-9]/g; + var num = number.match(re); + if(num && (num.length == number.length)) + return false; + else + return true; +} + +function onAccept() +{ + try { + var pref_string_content = ""; + var pref_string_title = ""; + + var description = document.getElementById("description").value; + var hostname = cleanUpHostName(document.getElementById("hostname").value); + var port = document.getElementById("port").value; + var secure = document.getElementById("secure"); + var results = document.getElementById("results").value; + var errorValue = null; + var saslMechanism = ""; + if ((!description) || (description.trim() == "")) + errorValue = "invalidName"; + else if (!isLegalHostNameOrIP(hostname)) + errorValue = "invalidHostname"; + // XXX write isValidDn and call it on the dn string here? + else if (port && hasCharacters(port)) + errorValue = "invalidPortNumber"; + else if (results && hasCharacters(results)) + errorValue = "invalidResults"; + if (!errorValue) { + // XXX Due to the LDAP c-sdk pass a dummy url to the IO service, then + // update the parts (bug 473351). + let ldapUrl = Services.io.newURI( + (secure.checked ? "ldaps://" : "ldap://") + "localhost/dc=???", null, null) + .QueryInterface(Components.interfaces.nsILDAPURL); + + ldapUrl.host = hostname; + ldapUrl.port = port ? port : + (secure.checked ? kDefaultSecureLDAPPort : + kDefaultLDAPPort); + ldapUrl.dn = document.getElementById("basedn").value; + ldapUrl.scope = document.getElementById("one").selected ? + Components.interfaces.nsILDAPURL.SCOPE_ONELEVEL : + Components.interfaces.nsILDAPURL.SCOPE_SUBTREE; + + ldapUrl.filter = document.getElementById("search").value; + if (document.getElementById("GSSAPI").selected) { + saslMechanism = "GSSAPI"; + } + + // check if we are modifying an existing directory or adding a new directory + if (gCurrentDirectory) { + gCurrentDirectory.dirName = description; + gCurrentDirectory.lDAPURL = ldapUrl.QueryInterface(Components.interfaces.nsILDAPURL); + window.opener.gNewServerString = gCurrentDirectory.dirPrefId; + } + else { // adding a new directory + window.opener.gNewServerString = + MailServices.ab.newAddressBook(description, ldapUrl.spec, kLDAPDirectory); + } + + // XXX This is really annoying - both new/modify Address Book don't + // give us back the new directory we just created - so go find it from + // rdf so we can set a few final things up on it. + var targetURI = "moz-abldapdirectory://" + window.opener.gNewServerString; + var theDirectory = + MailServices.ab.getDirectory(targetURI) + .QueryInterface(Components.interfaces.nsIAbLDAPDirectory); + + theDirectory.maxHits = results; + theDirectory.authDn = document.getElementById("login").value; + theDirectory.saslMechanism = saslMechanism; + + window.opener.gNewServer = description; + // set window.opener.gUpdate to true so that LDAP Directory Servers + // dialog gets updated + window.opener.gUpdate = true; + } else { + var addressBookBundle = document.getElementById("bundle_addressBook"); + + Services.prompt.alert(window, + document.title, + addressBookBundle.getString(errorValue)); + return false; + } + } catch (outer) { + dump("Internal error in pref-directory-add.js:onAccept() " + outer + "\n"); + } + return true; +} + +function onCancel() +{ + window.opener.gUpdate = false; +} + + +// called by Help button in platform overlay +function doHelpButton() +{ + openHelp("mail-ldap-properties"); +} + +// Sets the download button state for offline or online. +// This function should only be called for ldap edit dialogs. +function setDownloadOfflineOnlineState(isOffline) +{ + if (isOffline) + { + // Disable the download button and add some text indicating why. + document.getElementById("downloadWarningMsg").textContent = document. + getElementById("bundle_addressBook"). + getString("abReplicationOfflineWarning"); + } + document.getElementById("downloadWarningMsg").hidden = !isOffline; + document.getElementById("download").disabled = isOffline; +} diff --git a/mailnews/addrbook/prefs/content/pref-directory-add.xul b/mailnews/addrbook/prefs/content/pref-directory-add.xul new file mode 100644 index 000000000..33fe0cd31 --- /dev/null +++ b/mailnews/addrbook/prefs/content/pref-directory-add.xul @@ -0,0 +1,152 @@ +<?xml version="1.0"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/pref-directory-add.dtd"> + +<dialog id="addDirectory" + style="width: &newDirectoryWidth;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="Startup();" + onunload="onUnload();" + buttons="accept,cancel" + ondialogaccept="return onAccept();" + ondialogcancel="return onCancel();"> + + <script type="application/javascript" src="chrome://messenger/content/addressbook/pref-directory-add.js"/> + <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/> + <stringbundle id="bundle_replication" src="chrome://messenger/locale/addressbook/replicationProgress.properties"/> + + <keyset id="keyset"/> + <vbox id="editDirectory"> + + <tabbox style="margin:5px"> + <tabs id="directoryTabBox"> + <tab label="&General.tab;"/> + <tab label="&Offline.tab;"/> + <tab label="&Advanced.tab;"/> + </tabs> + + <tabpanels id="directoryTabPanels" flex="1"> + <vbox> + <grid flex="1"> + <columns> + <column/> + <column flex="1"/> + <column/> + </columns> + + <rows> + <row align="center"> + <label value="&directoryName.label;" accesskey="&directoryName.accesskey;" + control="description"/> + <textbox id="description" flex="1"/> + <spacer flex="1"/> + </row> + <row align="center"> + <label value="&directoryHostname.label;" accesskey="&directoryHostname.accesskey;" + control="hostname"/> + <textbox id="hostname" flex="1" disableiflocked="true" class="uri-element"/> + <spacer flex="1"/> + </row> + <row align="center"> + <label value="&directoryBaseDN.label;" + accesskey="&directoryBaseDN.accesskey;" + control="basedn"/> + <vbox> + <textbox id="basedn" disableiflocked="true" class="uri-element"/> + </vbox> + <button label="&findButton.label;" + accesskey="&findButton.accesskey;" disabled="true"/> + </row> + <row align="center"> + <label value="&portNumber.label;" + accesskey="&portNumber.accesskey;" + control="port"/> + <hbox> + <textbox id="port" type="number" size="5" min="1" + max="65535" hidespinbuttons="true" + disableiflocked="true"/> + </hbox> + </row> + <row align="center"> + <label value="&directoryLogin.label;" + accesskey="&directoryLogin.accesskey;" + control="login"/> + <textbox id="login" flex="1" class="uri-element"/> + </row> + </rows> + </grid> + <separator/> + <checkbox id="secure" label="&directorySecure.label;" + accesskey="&directorySecure.accesskey;" + oncommand="onSecure();" disableiflocked="true"/> + </vbox> + <vbox> + <description>&offlineText.label;</description> + <separator/> + <hbox> + <button id="download" oncommand="DownloadNow();"/> + <spacer flex="1"/> + </hbox> + <description id="downloadWarningMsg" hidden="true" class="error"/> + <description id="replicationProgressText" hidden="true"/> + + <progressmeter id="replicationProgressMeter" mode="normal" value="0" hidden="true"/> + </vbox> + <grid> + <columns> + <column/> + <column flex="1"/> + </columns> + + <rows> + <row align="center"> + <label value="&return.label;" + accesskey="&return.accesskey;" + control="results"/> + <hbox align="center"> + <textbox id="results" type="number" size="10" min="1" + max="2147483647" increment="10"/> + <label value="&results.label;"/> + </hbox> + </row> + <row align="center"> + <label value="&scope.label;" control="scope" accesskey="&scope.accesskey;"/> + <radiogroup id="scope" orient="horizontal"> + <radio id="one" value="1" label="&scopeOneLevel.label;" + disableiflocked="true" accesskey="&scopeOneLevel.accesskey;"/> + <radio id="sub" value="2" label="&scopeSubtree.label;" + disableiflocked="true" accesskey="&scopeSubtree.accesskey;"/> + </radiogroup> + </row> + <row> + <label value="&searchFilter.label;" + accesskey="&searchFilter.accesskey;" + control="search"/> + <textbox id="search" multiline="true" flex="1" disableiflocked="true"/> + </row> + <row align="center"> + <label value="&saslMechanism.label;" control="saslMechanism" accesskey="&saslMechanism.accesskey;"/> + <menulist id="saslMechanism"> + <menupopup> + <menuitem id="Simple" value="" + label="&saslOff.label;" + accesskey="&saslOff.accesskey;"/> + <menuitem id="GSSAPI" value="GSSAPI" + label="&saslGSSAPI.label;" + accesskey="&saslGSSAPI.accesskey;"/> + </menupopup> + </menulist> + </row> + </rows> + </grid> + </tabpanels> + </tabbox> + </vbox> + +</dialog> + diff --git a/mailnews/addrbook/prefs/content/pref-editdirectories.js b/mailnews/addrbook/prefs/content/pref-editdirectories.js new file mode 100644 index 000000000..dfb3e0559 --- /dev/null +++ b/mailnews/addrbook/prefs/content/pref-editdirectories.js @@ -0,0 +1,142 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource:///modules/mailServices.js"); + +// Listener to refresh the list items if something changes. In all these +// cases we just rebuild the list as it is easier than searching/adding in the +// correct places an would be an infrequent operation. +var gAddressBookAbListener = { + onItemAdded: function(parentDir, item) { + if (item instanceof Components.interfaces.nsIAbDirectory) { + fillDirectoryList(); + } + }, + onItemRemoved: function(parentDir, item) { + if (item instanceof Components.interfaces.nsIAbDirectory) { + fillDirectoryList(); + } + }, + onItemPropertyChanged: function(item, property, oldValue, newValue) { + if (item instanceof Components.interfaces.nsIAbDirectory) { + fillDirectoryList(); + } + } +}; + +function onInitEditDirectories() +{ + // For AbDeleteDirectory in abCommon.js + gAddressBookBundle = document.getElementById("bundle_addressBook"); + + // If the pref is locked disable the "Add" button + if (Services.prefs.prefIsLocked("ldap_2.disable_button_add")) + document.getElementById("addButton").setAttribute('disabled', true); + + // Fill out the directory list + fillDirectoryList(); + + const nsIAbListener = Components.interfaces.nsIAbListener; + // Add a listener so we can update correctly if the list should change + MailServices.ab.addAddressBookListener(gAddressBookAbListener, + nsIAbListener.itemAdded | + nsIAbListener.directoryRemoved | + nsIAbListener.itemChanged); +} + +function onUninitEditDirectories() +{ + MailServices.ab.removeAddressBookListener(gAddressBookAbListener); +} + +function fillDirectoryList() +{ + var abList = document.getElementById("directoriesList"); + + // Empty out anything in the list + while (abList.hasChildNodes()) + abList.lastChild.remove(); + + // Init the address book list + let directories = MailServices.ab.directories; + let holdingArray = []; + while (directories && directories.hasMoreElements()) { + let ab = directories.getNext(); + if (ab instanceof Components.interfaces.nsIAbDirectory && ab.isRemote) + holdingArray.push(ab); + } + + holdingArray.sort(function (a, b) { return a.dirName.localeCompare(b.dirName); }); + + holdingArray.forEach(function (ab) { + var item = document.createElement('listitem'); + item.setAttribute("label", ab.dirName); + item.setAttribute("value", ab.URI); + + abList.appendChild(item); + }); +} + +function selectDirectory() +{ + var abList = document.getElementById("directoriesList"); + var editButton = document.getElementById("editButton"); + var removeButton = document.getElementById("removeButton"); + + if (abList && abList.selectedItem) { + editButton.removeAttribute("disabled"); + + // If the disable delete button pref for the selected directory is set, + // disable the delete button for that directory. + let disable = false; + let ab = MailServices.ab.getDirectory(abList.value); + try { + disable = Services.prefs.getBoolPref(ab.dirPrefId + ".disable_delete"); + } + catch(ex){ + // If this preference is not set, it's ok. + } + if (disable) + removeButton.setAttribute("disabled", true); + else + removeButton.removeAttribute("disabled"); + } + else { + editButton.setAttribute("disabled", true); + removeButton.setAttribute("disabled", true); + } +} + +function dblClickDirectory(event) +{ + // We only care about left click events. + if (event.button != 0) + return; + + editDirectory(); +} + +function editDirectory() +{ + var abList = document.getElementById("directoriesList"); + + if (abList && abList.selectedItem) { + let abURI = abList.value; + let ab = MailServices.ab.getDirectory(abURI); + + window.openDialog(ab.propertiesChromeURI, "editDirectory", + "chrome,modal=yes,resizable=no", + { selectedDirectory: ab }); + } +} + +function removeDirectory() +{ + var abList = document.getElementById("directoriesList"); + + if (abList && abList.selectedItem) + AbDeleteDirectory(abList.value); +} diff --git a/mailnews/addrbook/prefs/content/pref-editdirectories.xul b/mailnews/addrbook/prefs/content/pref-editdirectories.xul new file mode 100644 index 000000000..43ca17a95 --- /dev/null +++ b/mailnews/addrbook/prefs/content/pref-editdirectories.xul @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/pref-directory.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + id="editDirectories" + title="&pref.ldap.window.title;" + buttons="accept" + onload="onInitEditDirectories();" + onunload="onUninitEditDirectories();"> + + <script type="application/javascript" + src="chrome://messenger/content/addressbook/abCommon.js"/> + <script type="application/javascript" + src="chrome://messenger/content/addressbook/pref-editdirectories.js"/> + + <stringbundle id="bundle_addressBook" + src="chrome://messenger/locale/addressbook/addressBook.properties"/> + + <label value="&directoriesText.label;" + accesskey="&directoriesText.accesskey;" control="directoriesList"/> + <hbox flex="1"> + <listbox id="directoriesList" flex="1" onselect="selectDirectory();" + ondblclick="dblClickDirectory(event);"/> + <vbox> + <button id="addButton" label="&addDirectory.label;" + accesskey="&addDirectory.accesskey;" + oncommand="AbNewLDAPDirectory();"/> + <button id="editButton" label="&editDirectory.label;" + accesskey="&editDirectory.accesskey;" disabled="true" + oncommand="editDirectory();"/> + <button id="removeButton" label="&deleteDirectory.label;" + accesskey="&deleteDirectory.accesskey;" disabled="true" + oncommand="removeDirectory();"/> + </vbox> + </hbox> +</dialog> |