summaryrefslogtreecommitdiff
path: root/swat.obsolete/apps/samba/utils/ldbbrowse.html
diff options
context:
space:
mode:
Diffstat (limited to 'swat.obsolete/apps/samba/utils/ldbbrowse.html')
-rw-r--r--swat.obsolete/apps/samba/utils/ldbbrowse.html734
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>
+