diff options
Diffstat (limited to 'swat.obsolete/apps/samba/utils/ldbbrowse.html')
-rw-r--r-- | swat.obsolete/apps/samba/utils/ldbbrowse.html | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/swat.obsolete/apps/samba/utils/ldbbrowse.html b/swat.obsolete/apps/samba/utils/ldbbrowse.html new file mode 100644 index 0000000000..1e6f21ee11 --- /dev/null +++ b/swat.obsolete/apps/samba/utils/ldbbrowse.html @@ -0,0 +1,734 @@ +<html> +<head> + <script type="text/javascript"> + /* + * Copyright: + * (C) 2006 by Derrell Lipman + * All rights reserved + * + * License: + * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/ + */ + </script> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>ldbbrowse</title> + <link type="text/css" rel="stylesheet" href="../../resource/css/layout-samba.css"/> + <!--[if IE]> + <link + type="text/css" rel="stylesheet" href="../../resource/css/layout-samba_ie.css"/> + <![endif]--> + <script type="text/javascript" src="../../script/qx.js"></script> +</head> +<body> + <script type="text/javascript" src="../../script/layout-samba.js"></script> + +<div style="position: absolute; background-color: transparent; right:0; top:0; z-index: 1000000001;"> + <img src="../../../images/logo.png"> +</div> +<div id="demoHead"> + ldbbrowse +</div> + + +<script type="text/javascript"> + +// All of our variables which must be in a global scope will be part of this +// object +globals = new Object(); + +// Name of database to use +globals.dbFile = "sam.ldb"; + +// No database is yet open +globals.dbHandle = null; + + +function setAppearances() +{ + // Modify the default appearance of a ComboBox for use in Search tab: + // use more of the available width. + // + // If we had multiple uses, we'd create a new appearance which didn't + // contain a width. That way, we'd be able to assign a specific width to + // each ComboBox instance. Since we don't have multiple of them, we can + // just modify this default appearance. + // + // See http://qooxdoo.org/documentation/user_manual/appearance for an + // explanation of what's going on here. The missing significant point in + // the discussion is that in the current qooxdoo appearance + // implementation, it's not possible to override a specific widget's + // appearance with explicit settings just for that widget (stupid!). I + // expect that to change in a future version. + var appMgr = qx.manager.object.AppearanceManager.getInstance(); + var theme = appMgr.getAppearanceTheme(); + var appearance = theme._appearances["combo-box"]; + if (! appearance) + { + return; + } + var oldInitial = appearance.initial; + appearance.initial = function(vTheme) + { + var res = oldInitial ? oldInitial.apply(this, arguments) : {}; + res.width = "80%"; + return res; + } +} + +function setupTabs(clientDocument) +{ + // Create and position the tabview + var tabView_ = new qx.ui.pageview.tabview.TabView; + tabView_.set( + { + top: 100, + left: 20, + right: 300, + bottom: 30 + }); + + // Create each of the tabs + var tabView_Search = + new qx.ui.pageview.tabview.TabViewButton("Search"); + var tabView_Browse = + new qx.ui.pageview.tabview.TabViewButton("Browse"); + + // Specify the initially-selected tab + tabView_Search.setChecked(true); + + // Add each of the tabs to the tabview + tabView_.getBar().add(tabView_Search, + tabView_Browse); + + // Create the pages to display when each tab is selected + var tabViewPage_Search = + new qx.ui.pageview.tabview.TabViewPage(tabView_Search); + var tabViewPage_Browse = + new qx.ui.pageview.tabview.TabViewPage(tabView_Browse); + + // Build the search page + var searchResultsTable = buildPageSearch(tabViewPage_Search); + + // Provide access to the search results table + tabView_.searchResultTable = searchResultsTable; + + // Build the browse page + buildPageBrowse(tabViewPage_Browse); + + // Add the pages to the tabview + tabView_.getPane().add(tabViewPage_Search, + tabViewPage_Browse); + + // Add the tabview to the document + clientDocument.add(tabView_); + + // Give 'em what we built! + return tabView_; +} + +function buildPageSearch(page) +{ + // We need a vertical box layout for the various input fields + var vlayout = new qx.ui.layout.VerticalBoxLayout(); + vlayout.setWidth("100%"); + + // We need a horizontal box layout for the search combo box and its label + var hlayout = new qx.ui.layout.HorizontalBoxLayout(); + hlayout.setWidth("100%"); + hlayout.setHeight(25); + + // Create a label for the list of required attributes + var label = new qx.ui.basic.Atom("Search Expression:"); + label.setWidth(100); + label.setHorizontalChildrenAlign("right"); + + // Add the label to the horizontal layout + hlayout.add(label); + + // Create a combo box for entry of the search expression + var search = new qx.ui.form.ComboBox(); + search.getField().setWidth("100%"); + search.setEditable(true); + + // Add the combo box to the horizontal layout + hlayout.add(search); + + // Add the horizontal layout to the vertical layout + vlayout.add(hlayout); + + // We need a horizontal box layout for the base combo box and its label + hlayout = new qx.ui.layout.HorizontalBoxLayout(); + hlayout.setWidth("100%"); + hlayout.setHeight(25); + + // Create a label for the list of required attributes + var label = new qx.ui.basic.Atom("Base:"); + label.setWidth(100); + label.setHorizontalChildrenAlign("right"); + + // Add the label to the horizontal layout + hlayout.add(label); + + // Create a combo box for entry of the search expression + var base = new qx.ui.form.ComboBox(); + base.getField().setWidth("100%"); + base.setEditable(true); + + // Add the combo box to the horizontal layout + hlayout.add(base); + + // Add the horizontal layout to the vertical layout + vlayout.add(hlayout); + + // We need a horizontal box layout for scope radio buttons + hlayout = new qx.ui.layout.HorizontalBoxLayout(); + hlayout.setWidth("100%"); + hlayout.setHeight(25); + + // Create a label for the list of required attributes + var label = new qx.ui.basic.Atom("Scope:"); + label.setWidth(100); + label.setHorizontalChildrenAlign("right"); + + // Add the label to the horizontal layout + hlayout.add(label); + + // Create a radio button for each scope + var rbDefault = new qx.ui.form.RadioButton("Default", "default"); + var rbBase = new qx.ui.form.RadioButton("Base", "base"); + var rbOne = new qx.ui.form.RadioButton("One Level", "one"); + var rbSubtree = new qx.ui.form.RadioButton("Subtree", "subtree"); + + // Use a default of "Default" + rbBase.setChecked(true); + + // Add the radio buttons to the horizontal layout + hlayout.add(rbDefault, rbBase, rbOne, rbSubtree); + + // Group the radio buttons so only one in the group may be selected + var scope = new qx.manager.selection.RadioManager("scope", + [ + rbDefault, + rbBase, + rbOne, + rbSubtree + ]); + + // Right-justify the 'Find' button + var spacer = new qx.ui.basic.HorizontalSpacer; + hlayout.add(spacer); + + // Create the 'Find' button + var find = new qx.ui.form.Button('Find'); + hlayout.add(find); + + // Add the Find button line to the vertical layout + vlayout.add(hlayout); + + // We'll be setting url and service upon execute; no need to do it now. + var rpc = new qx.io.remote.Rpc(); + rpc.setTimeout(60000); + var mycall = null; + + find.addEventListener("execute", function() + { + // Set the URL and Service + rpc.setUrl("/services/"); + rpc.setServiceName("samba.ldb"); + rpc.setCrossDomain(false); + + find.setEnabled(false); + mycall = rpc.callAsync(function(result, ex, id) + { + mycall = null; + if (ex == null) + { + var rowData = []; + + // Track the maximum length of the attribute values + var maxLen = 0; + + if (result && result["length"]) + { + len = result["length"]; + for (var i = 0; i < result["length"]; i++) + { + var o = result[i]; + if (typeof(o) != "object") + { + alert("Found unexpected result, type " + + typeof(o) + + ", " + + o + + "\n"); + continue; + } + for (var field in o) + { + // skip dn and distinguishedName fields; + // they're shown in each row anyway. + if (field == "dn" || field == "distinguishedName") + { + continue; + } + + // If it's multi-valued (type is an array)... + if (typeof(o[field]) == "object") + { + // ... then add each value with same name + var a = o[field]; + for (var i = 0; i < a.length; i++) + { + if (a[i].length > maxLen) + { + maxLen = a[i].length; + } + rowData.push( [ + o["dn"], + field, + a[i] + ] ); + } + } + else // single-valued + { + // ... add its name and value to the table + // dataset + if (o[field].length > maxLen) + { + maxLen = o[field].length; + } + rowData.push( [ + o["dn"], + field, + o[field] + ] ); + } + } + + // Adjust the width of the value column based on + // maxLen + table.setColumnWidth(2, maxLen * 7); + + // Tell the table to use the new data + tableModel.setData(rowData); + } + } + else + { + alert("No rows returned."); + } + } + else + { + alert("Async(" + id + ") exception: " + ex); + } + find.setEnabled(true); + }, + "search", // method + globals.dbHandle, // database handle + search.getValue(), // search expression + base.getValue(), // baseDN + scope.getSelected().getValue(), // scope + [ "*" ]); // attributes + }); + + // Add the horizontal box layout to the page + page.add(vlayout); + + // Create a simple table model + var tableModel = new qx.ui.table.SimpleTableModel(); + tableModel.setColumns([ "Distinguished Name", "Attribute", "Value" ]); + + tableModel.setColumnEditable(0, false); + tableModel.setColumnEditable(1, false); + tableModel.setColumnEditable(2, false); + + // Create a table + var table = new qx.ui.table.Table(tableModel); + with (table) + { + set({ + top: 80, + left: 0, + right: 0, + bottom: 10, + statusBarVisible: false, + columnVisibilityButtonVisible: false + }); + setColumnWidth(0, 300); + setColumnWidth(1, 180); + setColumnWidth(2, 240); + setMetaColumnCounts([ 1, -1 ]); // h-scroll attribute and value together + }; + + page.add(table); + + return table; +} + +function buildPageBrowse(page) +{ + /* + * Reset the default of always showing the plus/minus symbol. The + * default is 'false'. We want to always display it for each folder + * (and then stop displaying it if we determine upon open that there + * are no contents). + */ + var constructor = qx.OO.classes["qx.ui.treefullcontrol.TreeFolder"]; + qx.Proto = constructor.prototype; + qx.OO.changeProperty( + { + name : "alwaysShowPlusMinusSymbol", + type : qx.constant.Type.BOOLEAN, + defaultValue : true + }); + + // Create a vertical splitpane for tree (top) and table (bottom) + var splitpane = new qx.ui.splitpane.VerticalSplitPane("1*", "2*"); + splitpane.setEdge(0); + + // Create a tree row structure for the tree root + var trs = qx.ui.treefullcontrol.TreeRowStructure.getInstance().standard(globals.dbFile); + + // Create the tree and set its characteristics + var tree = new qx.ui.treefullcontrol.Tree(trs); + tree.set( + { + backgroundColor: 255, + border: qx.renderer.border.BorderPresets.getInstance().inset, + overflow: "auto", + height: null, + top: 10, + left: 0, + right: 0, + bottom: 10, + open: false + }); + + var addChildren = function(parent, children, retrieve) + { + var t; + var trs; + var child; + + // Any children? + if (! children || children["length"] == 0) + { + // Nope. Allow parent's expand/contract button to be removed + parent.setAlwaysShowPlusMinusSymbol(false); + return; + } + + for (i = 0; i < children.length; i++) + { + var name; + + child = children[i]; + + // Determine name for new tree row. If first level, use entire + // DN. Otherwise, strip off first additional component. + if (retrieve == "defaultNamingContext") + { + name = child["defaultNamingContext"]; + } + else + { + name = child["dn"].split(",")[0]; + } + + // Build a standard tree row + trs = qx.ui.treefullcontrol.TreeRowStructure.getInstance().standard(name); + + // This row is a "folder" (it can have children) + t = new qx.ui.treefullcontrol.TreeFolder(trs); + + // Add this row to its parent + parent.add(t); + } + + // Force flushing of pending DOM updates. This is actually a + // work-around for a bug. Without this, occasionally, updates to the + // gui aren't displayed until some 'action' takes place, e.g. key + // press or mouse movement. + qx.ui.core.Widget.flushGlobalQueues(); + } + + // Prepare to issue RPC calls + var rpc = new qx.io.remote.Rpc(); + rpc.setTimeout(60000); + rpc.setUrl("/services/"); + rpc.setServiceName("samba.ldb"); + rpc.setCrossDomain(false); + + /* + * All subtrees will use this root node's event listeners. Create an + * event listener for an open while empty. + */ + tree.addEventListener( + qx.constant.Event.TREEOPENWHILEEMPTY, + function(e) + { + var parent = e.getData(); + var hierarchy = parent.getHierarchy(new Array()); + + parent.debug("Requesting children..."); + + // Strip off the root node + hierarchy.shift(); + + // Determine the children. Differs depending on root or otherwise + var attributes; + var scope; + var baseDN; + + // If parent is the root... + if (parent == tree) + { + // ... then we want the defaultNamingContext, ... + attributes = "defaultNamingContext"; + + // ... and we want only base scope + scope = "base"; + + // ... and an empty base DN + baseDN = ""; + } + else + { + // otherwise, retrieve the DN, + attributes = "dn"; + + // ... and we want one level of scope + scope = "one"; + + // ... and base DN is the parent + baseDN = hierarchy.reverse().join(","); + } + + mycall = rpc.callAsync(function(result, ex, id) + { + mycall = null; + if (ex == null) + { + parent.debug("Children obtained. Rendering..."); + addChildren(parent, result, attributes); + parent.debug("Rendering complete."); + } + else + { + alert("Async(" + id + ") exception: " + ex); + } + }, + "search", // method + globals.dbHandle, // database handle + "(objectclass=*)", // search expression + baseDN, // baseDN + scope, // scope + [ attributes ]); // attributes + }); + + /* + * All subtrees will use this root node's event listeners. Create an + * event listener for selection changed, to populate attribute/value table + */ + tree.getManager().addEventListener( + qx.constant.Event.CHANGESELECTION, + function(e) + { + var element = e.getData()[0]; + var hierarchy = element.getHierarchy(new Array()); + + // Strip off the root node + hierarchy.shift(); + + // Determine the children. Differs depending on root or otherwise + var attributes; + var scope; + var baseDN; + + // If element is the root... + if (element == tree) + { + // ... then just clear out the attribute/value table. + tableModel.setData([]); + return; + } + + // We want all attributes + attributes = "*"; + + // We want the attributes only for the current element + scope = "base"; + + // Base DN is the current element + baseDN = hierarchy.reverse().join(","); + + mycall = rpc.callAsync(function(result, ex, id) + { + mycall = null; + if (ex == null) + { + // If we received an empty list, ... + if (result == null) + { + // ... then just clear the attribute/value table. + tableModel.setData([]); + return; + } + + // Start with an empty table dataset + var rowData = []; + + // The result contains a single object: attributes + var attributes = result[0]; + + // Track the maximum length of the attribute values + var maxLen = 0; + + // For each attribute we received... + for (var attr in attributes) + { + // If it's multi-valued (type is an array)... + if (typeof(attributes[attr]) == "object") + { + // ... then add each value with same name + var a = attributes[attr]; + for (var i = 0; i < a.length; i++) + { + if (a[i].length > maxLen) + { + maxLen = a[i].length; + } + rowData.push([ attr, a[i] ]); + } + } + else // single-valued + { + // ... add its name and value to the table dataset + if (attributes[attr].length > maxLen) + { + maxLen = attributes[attr].length; + } + rowData.push([ attr, attributes[attr] ]); + } + } + + // Adjust the width of the value column based on maxLen + table.setColumnWidth(1, maxLen * 7); + + // Add the dataset to the table + tableModel.setData(rowData); + + // Force flushing of pending DOM updates. This is + // actually a work-around for a bug. Without this, + // occasionally, updates to the gui aren't displayed until + // some 'action' takes place, e.g. key press or mouse + // movement. + qx.ui.core.Widget.flushGlobalQueues(); + } + else + { + alert("Async(" + id + ") exception: " + ex); + } + }, + "search", // method + globals.dbHandle, // database handle + "(objectclass=*)", // search expression + baseDN, // baseDN + scope, // scope + [ attributes ]); // attributes + }); + + + + + // Add the tree to the page. + splitpane.addTop(tree); + + // Create a simple table model + var tableModel = new qx.ui.table.SimpleTableModel(); + tableModel.setColumns([ "Attribute", "Value" ]); + + tableModel.setColumnEditable(0, false); + tableModel.setColumnEditable(1, false); + + // Create a table + var table = new qx.ui.table.Table(tableModel); + with (table) { + set({ + top: 10, + left: 0, + right: 0, + bottom: 10, + statusBarVisible: false, + columnVisibilityButtonVisible: false + }); + setColumnWidth(0, 200); + setColumnWidth(1, 440); + setMetaColumnCounts([1, -1]); + }; + + // Add the table to the bottom portion of the splitpane + splitpane.addBottom(table); + + // Add the first splitpane to the page + page.add(splitpane); +} + +qx.core.Init.getInstance().defineMain( + function() + { + // Enable JSON-RPC debugging + qx.Settings.setCustomOfClass("qx.io.Json", "enableDebug", true); + + if (false) + { + // We'd like all table columns to do "live resize". Unfortunately, + // it's too slow. Maybe someone wants to work on it... + var constructor = qx.OO.classes["qx.ui.table.TablePaneScroller"]; + qx.Proto = constructor.prototype; + qx.OO.changeProperty( + { + name : "liveResize", + type : qx.constant.Type.BOOLEAN, + defaultValue : true + }); + } + + // Set appearances for this application + setAppearances(); + + // Get the client document + var clientDocument = qx.ui.core.ClientDocument.getInstance(); + + // Create the tabs and their windows, and attach to client document + var tabView = setupTabs(clientDocument); + + // Open a database connection. Uses the dangerous sync request. + var rpc = new qx.io.remote.Rpc(); + rpc.setTimeout(60000); + rpc.setUrl("/services/"); + rpc.setServiceName("samba.ldb"); + rpc.setCrossDomain(false); + + try + { + // Database handle + globals.dbHandle = rpc.callSync("connect", globals.dbFile); + } + catch (ex) + { + alert("Sync exception: " + ex); + } + }); +/* + * Local Variables: + * mode: c + * End: + */ +</script> + +</body> +</html> + |