summaryrefslogtreecommitdiff
path: root/webapps/swat
diff options
context:
space:
mode:
authorDerrell Lipman <derrell@samba.org>2006-12-30 05:09:59 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 14:30:29 -0500
commit1170417ceeb8c49a46cda522a38eaa71c9cae30c (patch)
treedbf4c3fdcdb1af928dbd55f642ea40f00d09283a /webapps/swat
parent23ccdca41670085da4486841b0d4900b4c8b02f3 (diff)
downloadsamba-1170417ceeb8c49a46cda522a38eaa71c9cae30c.tar.gz
samba-1170417ceeb8c49a46cda522a38eaa71c9cae30c.tar.bz2
samba-1170417ceeb8c49a46cda522a38eaa71c9cae30c.zip
r20414: Start to make SWAT usable by others. This is just a start...
(This used to be commit 26a34037a7ca6fbd05c5a6f7c2d5973e34bc6918)
Diffstat (limited to 'webapps/swat')
-rw-r--r--webapps/swat/Makefile247
-rw-r--r--webapps/swat/source/.ignore1
-rw-r--r--webapps/swat/source/class/swat/main/Gui.js205
-rw-r--r--webapps/swat/source/class/swat/main/Main.js94
-rw-r--r--webapps/swat/source/class/swat/module/AbstractModule.js172
-rw-r--r--webapps/swat/source/class/swat/module/AbstractModuleFsm.js211
-rw-r--r--webapps/swat/source/class/swat/module/Module.js74
-rw-r--r--webapps/swat/source/class/swat/module/documentation/Documentation.js59
-rw-r--r--webapps/swat/source/class/swat/module/documentation/Fsm.js70
-rw-r--r--webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js383
-rw-r--r--webapps/swat/source/class/swat/module/ldbbrowse/Gui.js632
-rw-r--r--webapps/swat/source/class/swat/module/ldbbrowse/LdbBrowse.js52
-rw-r--r--webapps/swat/source/class/swat/module/statistics/Fsm.js209
-rw-r--r--webapps/swat/source/class/swat/module/statistics/Gui.js464
-rw-r--r--webapps/swat/source/class/swat/module/statistics/Statistics.js44
-rw-r--r--webapps/swat/source/index.html14
16 files changed, 2931 insertions, 0 deletions
diff --git a/webapps/swat/Makefile b/webapps/swat/Makefile
new file mode 100644
index 0000000000..aa17420c73
--- /dev/null
+++ b/webapps/swat/Makefile
@@ -0,0 +1,247 @@
+###################################################################################
+# PUBLIC VARIABLES
+###################################################################################
+
+QOOXDOO = ../qooxdoo-0.6.3-sdk
+SCRIPTNAME = swat.js
+APPCLASS = swat.main.Main
+INCLUDEALL = false
+OPTIMIZESTRINGS = false
+OPTIMIZEVARIABLES = false
+SOURCELOADER=
+NICE=10
+LOCALINSTALLDIR = /usr/local/samba/share/swat/apps/swat
+
+
+###################################################################################
+# PRIVATE VARIABLES
+###################################################################################
+
+FRONTEND = $(QOOXDOO)/frontend
+FRAMEWORK = $(FRONTEND)/framework
+API = $(FRONTEND)/api
+CACHE = $(FRAMEWORK)/.cache
+GENERATOR = $(FRAMEWORK)/tool/generator.py
+
+
+ifeq ($(INCLUDEALL),false)
+ INCLUDE = --include $(APPCLASS)
+else
+ INCLUDE =
+endif
+
+ifeq ($(OPTIMIZESTRINGS),true)
+ OPTIMIZESTR = --optimize-strings
+else
+ OPTIMIZESTR =
+endif
+
+ifeq ($(OPTIMIZEVARIABLES),true)
+ OPTIMIZEVAR = --optimize-variables
+else
+ OPTIMIZEVAR =
+endif
+
+ifneq ($(SOURCELOADER),)
+ SOURCELDR = --source-loader-type $(SOURCELOADER)
+else
+ SOURCELDR =
+endif
+
+
+###################################################################################
+# DEFAULT TARGET
+###################################################################################
+
+all: build
+
+
+
+###################################################################################
+# COMMON TARGETS
+###################################################################################
+
+source: info-source \
+ generate-script-source \
+ generate-api-data
+
+build: info-build \
+ generate-script-build \
+ generate-api-data \
+ copy-build-files \
+ fix-build-rights
+
+api: generate-api-build generate-api-data
+ @echo
+ @echo " CREATE COPY OF HTML FILE"
+ @echo "----------------------------------------------------------------------------"
+ @echo " * Copying file..."
+ @cp -f $(API)/source/index.html api/index.html
+
+pretty: generate-pretty
+fix: generate-fix
+
+
+
+###################################################################################
+# CLEANUP TARGETS
+###################################################################################
+
+clean:
+ @echo
+ @echo " CLEANUP OF GENERATED FILES"
+ @echo "----------------------------------------------------------------------------"
+ @echo " * Deleting files..."
+ @rm -f source/script/$(SCRIPTNAME) build/script/$(SCRIPTNAME)
+
+realclean: clean
+ @echo
+ @echo " CLEANUP OF GENERATED FILES (REAL)"
+ @echo "----------------------------------------------------------------------------"
+ @echo " * Deleting files..."
+ @nice -n $(NICE) rm -rf build
+
+distclean: realclean
+ @echo
+ @echo " CLEANUP OF GENERATED FILES (DIST)"
+ @echo "----------------------------------------------------------------------------"
+ @echo " * Deleting files..."
+ @nice -n $(NICE) find . -name "*~" -o -name "*.bak" -o -name "*.old" | xargs rm -rf
+ @nice -n $(NICE) rm -rf $(CACHE)
+
+
+
+###################################################################################
+# GENERATOR TARGETS
+###################################################################################
+
+generate-script-source:
+ @chmod u+x $(GENERATOR) && nice -n $(NICE) $(GENERATOR) \
+ --script-input $(FRAMEWORK)/source/class \
+ --source-script-path ../$(FRAMEWORK)/source/class \
+ --script-input $(API)/source/class \
+ --source-script-path ../$(API)/source/class \
+ --script-input source/class \
+ --source-script-path class \
+ --generate-source-script $(SOURCELDR) \
+ $(INCLUDE) \
+ --source-script-file source/script/$(SCRIPTNAME) \
+ --define-runtime-setting qx.manager.object.AliasManager.resourceUri:../$(FRAMEWORK)/source/resource \
+ --cache-directory $(CACHE) \
+ --add-new-lines
+
+#
+# djl: --script-input begins a set for which --resource-input and
+# --resource-output apply. Since there is a --resource-input and a
+# --resource-output defined for one set, it must be defined for each set
+#
+generate-script-build:
+ @chmod u+x $(GENERATOR) && nice -n $(NICE) $(GENERATOR) \
+ --script-input $(FRAMEWORK)/source/class \
+ --resource-input $(FRAMEWORK)/source/resource \
+ --resource-output build/resource \
+ --script-input $(API)/source/class \
+ --resource-input $(API)/source/resource \
+ --resource-output build/resource \
+ --script-input source/class \
+ --resource-input source/resource \
+ --resource-output build/resource \
+ --generate-compiled-script \
+ $(INCLUDE) $(OPTIMIZESTR) $(OPTIMIZEVAR) \
+ --compiled-script-file build/script/$(SCRIPTNAME) \
+ --copy-resources \
+ --define-runtime-setting qx.manager.object.AliasManager.resourceUri:./resource \
+ --cache-directory $(CACHE) \
+ --add-new-lines
+
+generate-api-build:
+ @chmod u+x $(GENERATOR) && nice -n $(NICE) $(GENERATOR) \
+ --script-input $(FRAMEWORK)/source/class \
+ --resource-input $(FRAMEWORK)/source/resource \
+ --resource-output api/resource/qooxdoo \
+ --script-input $(API)/source/class \
+ --resource-input $(API)/source/resource \
+ --resource-output api/resource \
+ --include api \
+ $(OPTIMIZESTR) $(OPTIMIZEVAR) \
+ --generate-compiled-script \
+ --compiled-script-file api/script/api.js \
+ --define-runtime-setting qx.manager.object.AliasManager.resourceUri:resource/qooxdoo \
+ --define-runtime-setting api.Viewer.title:Swat \
+ --copy-resources \
+ --cache-directory $(CACHE)
+
+generate-api-data:
+ @chmod u+x $(GENERATOR) && nice -n $(NICE) $(GENERATOR) \
+ --script-input $(FRAMEWORK)/source/class \
+ --script-input $(API)/source/class \
+ --script-input source/class \
+ --generate-api-documentation \
+ --api-documentation-json-file build/script/data.js \
+ --cache-directory $(CACHE)
+
+generate-pretty:
+ @chmod u+x $(GENERATOR) && nice -n $(NICE) $(GENERATOR) \
+ --script-input source/class \
+ --script-input $(FRAMEWORK)/source/class \
+ --include-without-dependencies api.* \
+ --pretty-print \
+ --cache-directory $(CACHE)
+
+generate-fix:
+ @chmod u+x $(GENERATOR) && nice -n $(NICE) $(GENERATOR) \
+ --script-input source/class \
+ --script-input $(FRAMEWORK)/source/class \
+ --include-without-dependencies api.* \
+ --fix-source \
+ --cache-directory $(CACHE)
+
+
+
+###################################################################################
+# ADDITIONAL TARGETS
+###################################################################################
+
+copy-build-files:
+ @echo
+ @echo " CREATE COPY OF HTML FILE"
+ @echo "----------------------------------------------------------------------------"
+ @echo " * Copying file..."
+ @cp -f source/index.html build/index.html
+
+fix-build-rights:
+ @echo
+ @echo " FIX RIGHTS ON BUILD FILES"
+ @echo "----------------------------------------------------------------------------"
+ @echo " * Fixing file rights..."
+ @nice -n $(NICE) find build -type d | xargs chmod a+rx
+ @nice -n $(NICE) find build -type f | xargs chmod a+r
+
+
+
+
+
+###################################################################################
+# INFO TARGETS
+###################################################################################
+
+info-build:
+ @echo "****************************************************************************"
+ @echo " GENERATING SWAT BUILD"
+ @echo "****************************************************************************"
+
+info-source:
+ @echo "****************************************************************************"
+ @echo " GENERATING SWAT SOURCE"
+ @echo "****************************************************************************"
+
+###################################################################################
+# INSTALL TARGETS
+###################################################################################
+
+install:
+ @echo "****************************************************************************"
+ @echo " INSTALLING SWAT"
+ @echo "****************************************************************************"
+ @echo " * Installing swat files..."
+ @rsync -av --exclude=crystalsvg --delete build/ $(LOCALINSTALLDIR)
diff --git a/webapps/swat/source/.ignore b/webapps/swat/source/.ignore
new file mode 100644
index 0000000000..84f7e31d99
--- /dev/null
+++ b/webapps/swat/source/.ignore
@@ -0,0 +1 @@
+script
diff --git a/webapps/swat/source/class/swat/main/Gui.js b/webapps/swat/source/class/swat/main/Gui.js
new file mode 100644
index 0000000000..8d18f5f38a
--- /dev/null
+++ b/webapps/swat/source/class/swat/main/Gui.js
@@ -0,0 +1,205 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * The graphical user interface for the main menu
+ */
+qx.OO.defineClass("swat.main.Gui");
+
+qx.Class.currentCanvas = null;
+
+qx.Class.buildGui = function(moduleList)
+{
+ var o;
+
+ // Header colors
+ var topColor = new qx.renderer.color.Color("#ffff64");
+ var bottomColor = new qx.renderer.color.Color("#3878cd");
+
+ // Create the yellow bar at the top
+ o = new qx.ui.basic.Terminator();
+ o.set({
+ top: 0,
+ left: 0,
+ right: 0,
+ height: 40
+ });
+ o.setBackgroundColor(topColor);
+ o.addToDocument();
+
+ // Create the tag line
+ var title = new qx.ui.basic.Label("Samba Web Administration Tool");
+ title.set({
+ top: 12,
+ left: 20
+ });
+ title.setBackgroundColor(topColor);
+ title.setFont("bold");
+ title.addToDocument();
+
+ // Create a small black separator between the yellow and blue bars
+ o = new qx.ui.basic.Terminator();
+ o.set({
+ top: 40,
+ left: 0,
+ right: 0,
+ height: 1
+ });
+ o.setBackgroundColor("black");
+ o.addToDocument();
+
+ // Create the yellow bar at the top
+ o = new qx.ui.basic.Terminator();
+ o.set({
+ top: 41,
+ left: 0,
+ right: 0,
+ height: 60
+ });
+ o.setBackgroundColor(bottomColor);
+ o.addToDocument();
+
+ // Create the "Samba" image
+ o = new qx.ui.basic.Image("../../../images/logo.png");
+ o.set({
+ top: 16,
+ right: 20
+ });
+ o.setZIndex(100000000);
+ o.addToDocument();
+
+ // Create a menu bar
+ var menubar = new qx.ui.toolbar.ToolBar();
+ menubar.set({
+ top: 100,
+ height: 20,
+ left: 0,
+ right: 0
+ });
+ menubar.addToDocument();
+
+ // Create a menu item for selecting the requested module
+ var menubutton = new qx.ui.toolbar.MenuButton("Modules");
+ menubar.add(menubutton);
+
+ // Create a Command to be executed upon any menu selection
+ var command = new qx.client.Command();
+ // command.addEventListener("execute", fsm.eventListener, fsm);
+
+ // Create a radio manager for selecting one of the modules
+ var moduleManager = new qx.manager.selection.RadioManager("main.gui.Module");
+
+ // Create a menu containing the subitems
+ var menu = new qx.ui.menu.Menu();
+
+ // We'll also track the current module's canvas in the modules object
+ swat.main.Gui.currentCanvas = null;
+
+ // For each menu item...
+ for (moduleName in moduleList)
+ {
+ // create a radio button menu item
+ o = new qx.ui.menu.RadioButton(moduleName, null, command);
+
+ // Associate this button menu item with the module list
+ o.moduleName = moduleName;
+
+ // Associate the menu item with the radio manager
+ moduleManager.add(o);
+
+ // Create this module's canvas
+ var canvas = new qx.ui.layout.CanvasLayout();
+ canvas.set({
+ top: 120,
+ bottom: 0,
+ left: 0,
+ right: 0
+ });
+ canvas.setBackgroundColor("white");
+ canvas.setDisplay(false); // initially not displayed
+
+ var fsm = moduleList[moduleName].fsm;
+ fsm.addObject("swat.module.canvas", canvas);
+ canvas.addEventListener("appear", fsm.eventListener, fsm);
+ canvas.addEventListener("disappear", fsm.eventListener, fsm);
+
+ // Save the canvas
+ moduleList[moduleName].canvas = canvas;
+
+ // Add the canvas to the document
+ canvas.addToDocument();
+
+ // When a Module menu item is selected:
+ o.addEventListener("changeChecked", function(e)
+ {
+ var canvas = moduleList[this.moduleName].canvas;
+
+ // If there's a current canvas, ...
+ if (swat.main.Gui.currentCanvas)
+ {
+ // ... then remove display of it.
+ swat.main.Gui.currentCanvas.setDisplay(false);
+
+ // Dispatch an event on the canvas to notify old
+ // module it's coming into disuse.
+ canvas.createDispatchEvent("disappear");
+ }
+
+ // If we are being selected...
+ if (e.getData())
+ {
+ // then display our canvas
+ var canvas = moduleList[this.moduleName].canvas;
+ canvas.setDisplay(true);
+
+ // Track the current canvas (now ours)
+ swat.main.Gui.currentCanvas = canvas;
+
+ // Dispatch an event on the canvas to notify new
+ // module it's coming into use.
+ canvas.createDispatchEvent("appear");
+
+ // Set the application title
+ title.setHtml("<span>" +
+ "Samba Web Administration Tool" +
+ " &raquo; " +
+ this.moduleName +
+ "</span>");
+
+ // Set the browser title as well
+ document.title =
+ "Swat: " + this.moduleName;
+
+ }
+ });
+
+ // Add the menu item to the menu
+ menu.add(o);
+ }
+
+ // We've built a complete menu. Add it to the document.
+ menu.addToDocument();
+
+ // Specify that the menu is to be displayed upon menu button selection
+ menubutton.setMenu(menu);
+
+ // Create a menu item for selecting debug options
+ var menubutton = new qx.ui.toolbar.MenuButton("Debug");
+ menubar.add(menubutton);
+
+ // Create a menu containing the subitems
+ var menu = new qx.ui.menu.Menu;
+ var menu_01 = new qx.ui.menu.Button("Show Debug Window", null, command);
+ menu.add(menu_01);
+ menu.addToDocument();
+
+ // Specify that the menu is to be displayed upon menu button selection
+ menubutton.setMenu(menu);
+
+};
diff --git a/webapps/swat/source/class/swat/main/Main.js b/webapps/swat/source/class/swat/main/Main.js
new file mode 100644
index 0000000000..dc3bbc031e
--- /dev/null
+++ b/webapps/swat/source/class/swat/main/Main.js
@@ -0,0 +1,94 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/*
+#require(swat.module.Module)
+#require(swat.module.AbstractModule)
+*/
+
+/**
+ * Swat main menu
+ */
+qx.OO.defineClass("swat.main.Main", qx.component.AbstractApplication,
+function()
+{
+ qx.component.AbstractApplication.call(this);
+});
+
+/*
+ * Register our supported modules
+ */
+
+//#require(swat.module.statistics.Statistics)
+new swat.module.Module("Statistics",
+ swat.module.statistics.Statistics);
+
+//#require(swat.module.ldbbrowse.LdbBrowse)
+new swat.module.Module("LDB Browser",
+ swat.module.ldbbrowse.LdbBrowse);
+
+//#require(swat.module.documentation.Documentation)
+//#require(api.Viewer)
+new swat.module.Module("API Documentation",
+ swat.module.documentation.Documentation);
+
+
+/*
+---------------------------------------------------------------------------
+ METHODS
+---------------------------------------------------------------------------
+*/
+
+qx.Proto.initialize = function()
+{
+ // Set the resource URI
+ qx.Settings.setCustom("resourceUri", "./resource");
+
+ // Turn on JSON debugging for the time being
+ qx.Settings.setCustomOfClass("qx.io.Json", "enableDebug", true);
+
+ // For each module...
+ var moduleList = swat.module.Module.getList();
+ for (moduleName in moduleList)
+ {
+ // ... call the module's buildInitialFsm() function
+ var module = moduleList[moduleName]["class"].getInstance();
+ module.buildInitialFsm(moduleList[moduleName]);
+ }
+};
+
+
+qx.Proto.main = function()
+{
+ var moduleList = swat.module.Module.getList();
+
+ // Initialize the gui for the main menu
+ swat.main.Gui.buildGui(moduleList);
+
+ // Similarly, now that we have a canvas for each module, ...
+ for (moduleName in moduleList)
+ {
+ // ... call the module's buildInitialGui() function
+ var module = moduleList[moduleName]["class"].getInstance();
+ module.buildInitialGui(moduleList[moduleName]);
+ }
+};
+
+
+qx.Proto.finalize = function()
+{
+ // Call each module's finalization function
+ var moduleList = swat.module.Module.getList();
+ for (moduleName in moduleList)
+ {
+ var module = moduleList[moduleName]["class"].getInstance();
+ module.finalize(moduleList[moduleName]);
+ }
+};
+
diff --git a/webapps/swat/source/class/swat/module/AbstractModule.js b/webapps/swat/source/class/swat/module/AbstractModule.js
new file mode 100644
index 0000000000..19bcc88a13
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/AbstractModule.js
@@ -0,0 +1,172 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Abstract Module class. All modules should extend this class.
+ */
+qx.OO.defineClass("swat.module.AbstractModule", qx.core.Object,
+function()
+{
+ qx.core.Object.call(this);
+ this.debug("AbstractModule constructor");
+});
+
+
+/**
+ * Build the initial finite state machine.
+ *
+ * In order to prevent long load times, as minimal as possible of an initial
+ * FSM should be created. The FSM will receive an "appear" event when the
+ * module is first selected (and each subsequent time), and the FSM can use
+ * that event to build the complete FSM.
+ *
+ * @param module {swat.module.Module}
+ * The module descriptor for the module.
+ */
+qx.Proto.buildInitialFsm = function(module)
+{
+ // Create a new finite state machine
+ var fsm = new qx.util.fsm.FiniteStateMachine(module.name);
+
+ // For this simple example application, show all debug messages.
+ qx.Settings.setCustomOfClass(
+ "qx.util.fsm.FiniteStateMachine",
+ "debugFlags",
+ (qx.util.fsm.FiniteStateMachine.DebugFlags.EVENTS |
+ qx.util.fsm.FiniteStateMachine.DebugFlags.TRANSITIONS |
+ qx.util.fsm.FiniteStateMachine.DebugFlags.FUNCTION_DETAIL |
+ qx.util.fsm.FiniteStateMachine.DebugFlags.OBJECT_NOT_FOUND));
+
+ /*
+ * State: Idle
+ *
+ * Transition on:
+ * "appear" on swat.module.canvas
+ */
+ var state = new qx.util.fsm.State(
+ "State_Idle",
+ {
+ "events" :
+ {
+ // When we get an appear event the first time, run the transition
+ // that will load the module's finite state machine and graphical
+ // user interface.
+ "appear" :
+ {
+ "swat.module.canvas" :
+ "Transition_Idle_to_Idle_Load_Gui"
+ }
+ }
+ });
+ fsm.addState(state);
+
+ /*
+ * Transition: Idle to (replaced) Idle
+ *
+ * Cause: "appear" on canvas for the first time
+ *
+ * Action:
+ * Load module's finite state machine and graphical user interface
+ */
+ var thisModule = this;
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_Idle_Load_Gui",
+ {
+ "nextState" :
+ qx.util.fsm.FiniteStateMachine.StateChange.CURRENT_STATE,
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ // Make the "Loading" message go away. (We need to learn how to
+ // remove it entirely. Just doing canvas.removeAll() leaves
+ // something in the widget queue and we get spurious error
+ // messages.)
+ var children = module.canvas.getVisibleChildren();
+ for (var child in children)
+ {
+ children[child].hide();
+ }
+
+ // Call the module's initialAppear function to build FSM and GUI.
+ // That function should *replace* this state, State_Idle, to which
+ // we'll transition.
+ thisModule.initialAppear(module);
+ }
+ });
+ state.addTransition(trans);
+
+ // Save the finite state machine for this module
+ module.fsm = fsm;
+
+ // Save the module descriptor in the finite state machine
+ fsm.addObject("swat.module.module", module);
+
+ // Create an RPC object for use by this module
+ module.rpc = new qx.io.remote.Rpc();
+ module.rpc.setUrl("/services/");
+ module.rpc.setTimeout(10000);
+ module.rpc.setCrossDomain(false);
+ module.rpc.addEventListener("completed", fsm.eventListener, fsm);
+ module.rpc.addEventListener("failed", fsm.eventListener, fsm);
+ module.rpc.addEventListener("timeout", fsm.eventListener, fsm);
+ module.rpc.addEventListener("aborted", fsm.eventListener, fsm);
+ fsm.addObject("swat.module.rpc", module.rpc);
+
+ // Start the finite state machine
+ fsm.start();
+};
+
+/**
+ * Build the initial graphical user interface.
+ *
+ * In order to prevent long load times, as minimal as possible of an initial
+ * GUI should be created. Generally, this is just a "Loading..." message.
+ *
+ * @param module {Object}
+ * An object containing at least the following properties:
+ * fsm -
+ * The finite state machine for this module. It should be filled in
+ * by this function.
+ * canvas -
+ * The canvas on which to create the gui for this module
+ * name -
+ * The name of this module
+ * class -
+ * The class for this module
+ *
+ */
+qx.Proto.buildInitialGui = function(module)
+{
+ // For now, just create the "Loading" text
+ var o = new qx.ui.basic.Label("Loading module '" + module.name + "'...");
+ o.set({
+ top: 12,
+ left: 20
+ });
+ o.setFont("bold");
+ module.canvas.add(o);
+};
+
+qx.Proto.finalize = function(module)
+{
+ this.debug("AbstractModule.finalize()");
+};
+
+
+/*
+---------------------------------------------------------------------------
+ DEFER SINGLETON INSTANCE
+---------------------------------------------------------------------------
+*/
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/AbstractModuleFsm.js b/webapps/swat/source/class/swat/module/AbstractModuleFsm.js
new file mode 100644
index 0000000000..57b8baaac9
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/AbstractModuleFsm.js
@@ -0,0 +1,211 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Common facilities for modules' finite state machines. Each module's FSM
+ * should extend this class.
+ */
+qx.OO.defineClass("swat.module.AbstractModuleFsm", qx.core.Object, function()
+{
+ qx.core.Object.call(this);
+});
+
+
+qx.Proto.buildFsm = function(module)
+{
+ throw new Error("Module must overload buildFsm() " +
+ "to build its custom finite state machine.");
+};
+
+qx.Proto.addAwaitRpcResultState = function(module)
+{
+ var fsm = module.fsm;
+
+ /*
+ * State: AwaitRpcResult
+ *
+ * Actions upon entry:
+ * - enable any objects in group "swat.module.fsmUtils.enable_during_rpc"
+ * - disable any objects in group "swat.module.fsmUtils.disable_during_rpc"
+ *
+ * Actions upon exit:
+ * - disable any objects in group "swat.module.fsmUtils.enable_during_rpc"
+ * - enable any objects in group "swat.module.fsmUtils.disable_during_rpc"
+ *
+ * Transition on:
+ * "completed" (on RPC)
+ * "failed" (on RPC)
+ * "execute" on swat.module.fsmUtils.abort_rpc
+ */
+ var state = new qx.util.fsm.State(
+ "State_AwaitRpcResult",
+ {
+ "autoActionsBeforeOnentry" :
+ {
+ // The name of a function.
+ "setEnabled" :
+ [
+ {
+ // We want to enable objects in the group
+ // swat.module.fsmUtils.enable_during_rpc
+ "parameters" : [ true ],
+
+ // Call this.getObject(<object>).setEnabled(true) on
+ // state entry, for each <object> in the group called
+ // "swat.module.fsmUtils.enable_during_rpc".
+ "groups" : [ "swat.module.fsmUtils.enable_during_rpc" ]
+ },
+
+ {
+ // We want to disable objects in the group
+ // swat.module.fsmUtils.disable_during_rpc
+ "parameters" : [ false ],
+
+ // Call this.getObject(<object>).setEnabled(false) on
+ // state entry, for each <object> in the group called
+ // "swat.module.fsmUtils.disable_during_rpc".
+ "groups" : [ "swat.module.fsmUtils.disable_during_rpc" ]
+ }
+ ]
+ },
+
+ "autoActionsBeforeOnexit" :
+ {
+ // The name of a function.
+ "setEnabled" :
+ [
+ {
+ // We want to re-disable objects we had enabled, in the group
+ // swat.module.fsmUtils.enable_during_rpc
+ "parameters" : [ false ],
+
+ // Call this.getObject(<object>).setEnabled(false) on
+ // state entry, for each <object> in the group called
+ // "swat.module.fsmUtils.enable_during_rpc".
+ "groups" : [ "swat.module.fsmUtils.enable_during_rpc" ]
+ },
+
+ {
+ // We want to re-enable objects we had disabled, in the group
+ // swat.module.fsmUtils.disable_during_rpc
+ "parameters" : [ true ],
+
+ // Call this.getObject(<object>).setEnabled(true) on
+ // state entry, for each <object> in the group called
+ // "swat.module.fsmUtils.disable_during_rpc".
+ "groups" : [ "swat.module.fsmUtils.disable_during_rpc" ]
+ }
+ ]
+ },
+
+ "onentry" :
+ function(fsm, state)
+ {
+ // If we're coming from some other start...
+ if (fsm.getPreviousState() != "State_AwaitRpcResult")
+ {
+ // ... then push the previous state onto the state stack
+ fsm.pushState(false);
+ }
+ },
+
+ "events" :
+ {
+ "execute" :
+ {
+ "swat.module.fsmUtils.abort_rpc" :
+ "Transition_AwaitRpcResult_to_AwaitRpcResult_via_button_abort"
+ },
+
+ "completed" :
+ "Transition_AwaitRpcResult_to_PopStack_via_complete",
+
+ "failed" :
+ "Transition_AwaitRpcResult_to_PopStack_via_failed"
+ }
+ });
+ fsm.addState(state);
+
+ /*
+ * Transition: AwaitRpcResult to AwaitRpcResult
+ *
+ * Cause: "execute" on swat.module.fsmUtils.abort_rpc
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_AwaitRpcResult_to_AwaitRpcResult_via_button_abort",
+ {
+ "nextState" :
+ "State_AwaitRpcResult",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ // Get the request object
+ var request = fsm.getObject("swat.module.fsmUtils.request");
+
+ // Issue an abort for the pending request
+ request.abort();
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: AwaitRpcResult to PopStack
+ *
+ * Cause: "complete" (on RPC)
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_AwaitRpcResult_to_PopStack_via_complete",
+ {
+ "nextState" :
+ qx.util.fsm.FiniteStateMachine.StateChange.POP_STATE_STACK,
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ // Get the request object
+ var request = fsm.getObject("swat.module.fsmUtils.request");
+
+ // Generate the result for a completed request
+ request.setUserData("result",
+ {
+ type : "complete",
+ data : event.getData()
+ });
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: AwaitRpcResult to PopStack
+ *
+ * Cause: "failed" (on RPC)
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_AwaitRpcResult_to_PopStack_via_failed",
+ {
+ "nextState" :
+ qx.util.fsm.FiniteStateMachine.StateChange.POP_STATE_STACK,
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ // Get the request object
+ var request = fsm.getObject("swat.module.fsmUtils.request");
+
+ // Generate the result for a completed request
+ request.setUserData("result",
+ {
+ type : "failed",
+ data : event.getData()
+ });
+ }
+ });
+ state.addTransition(trans);
+};
diff --git a/webapps/swat/source/class/swat/module/Module.js b/webapps/swat/source/class/swat/module/Module.js
new file mode 100644
index 0000000000..e7180d2895
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/Module.js
@@ -0,0 +1,74 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * This class defines a module descriptor (the registration of a module) and
+ * maintains the list of modules that have been registered.
+ *
+ * A Module object contains the following public properties which may be
+ * accessed directly by name:
+ *
+ * fsm -
+ * The finite state machine for this module.
+ *
+ * canvas -
+ * The canvas on which to create the gui for this module
+ *
+ * name -
+ * The name of this module
+ *
+ * class -
+ * The class for this module
+ *
+ * @param moduleName {string}
+ * The name of the module being registered. This is the name that will
+ * appear in the Modules menu.
+ *
+ * @param class {class}
+ * The class which contains the module implementation. That class must
+ * extend swat.module.AbstractModule and implement a singleton interface
+ * that provides a public method called initialAppear() which takes this
+ * Module object as a parameter, and creates the finite state machine for
+ * the module (if applicable) and builds the graphical user interface for
+ * the module.
+ */
+qx.OO.defineClass("swat.module.Module", qx.core.Object,
+function(moduleName, class)
+{
+ qx.core.Object.call(this);
+
+ // Initialize commonly-used properties of a module
+ this.canvas = null;
+ this.fsm = null;
+ this.gui = null;
+
+ // Save the module name
+ this.name = moduleName;
+
+ // Save this class name
+ this.class = class;
+
+ // Add this new module to the module list.
+ swat.module.Module._list[moduleName] = this;
+});
+
+
+/**
+ * Return the list of modules
+ */
+qx.Class.getList = function()
+{
+ return swat.module.Module._list;
+};
+
+
+/**
+ * The list of modules which have been registered.
+ */
+qx.Class._list = { };
diff --git a/webapps/swat/source/class/swat/module/documentation/Documentation.js b/webapps/swat/source/class/swat/module/documentation/Documentation.js
new file mode 100644
index 0000000000..03191cacc2
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/documentation/Documentation.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class
+ */
+qx.OO.defineClass("swat.module.documentation.Documentation",
+ swat.module.AbstractModule,
+function()
+{
+ swat.module.AbstractModule.call(this);
+});
+
+
+/**
+ * Load the documentation data
+ *
+ * This function is called the first time a module is actually selected to
+ * appear. Creation of the module's GUI has been deferred until it was
+ * actually needed (now), so we need to create it.
+ *
+ * @param module {swat.module.Module}
+ * The module descriptor for the module.
+ */
+qx.Proto.initialAppear = function(module)
+{
+ qx.manager.object.AliasManager.getInstance().add("api", "./resource/image");
+
+ // Include CSS file.
+ // (This is the hard way; I can't get qx.dom.StyleSheet.includeFile to load)
+ var el = document.createElement("link");
+ el.type = "text/css";
+ el.rel = "stylesheet";
+ el.href = "./resource/css/apiviewer.css";
+ var head = document.getElementsByTagName("head")[0];
+ head.appendChild(el);
+
+ // avoid redundant naming by api viewer
+ qx.Settings.setCustomOfClass("apiviewer.Viewer", "title", "");
+
+ var viewer = new api.Viewer();
+ module.canvas.add(viewer);
+ viewer.load("script/data.js");
+
+ // Replace the existing (temporary) finite state machine with a null one
+ swat.module.documentation.Fsm.getInstance().buildFsm(module);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/documentation/Fsm.js b/webapps/swat/source/class/swat/module/documentation/Fsm.js
new file mode 100644
index 0000000000..9df878b4f1
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/documentation/Fsm.js
@@ -0,0 +1,70 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class finite state machine
+ */
+qx.OO.defineClass("swat.module.documentation.Fsm",
+ swat.module.AbstractModuleFsm,
+function()
+{
+ swat.module.AbstractModuleFsm.call(this);
+});
+
+
+qx.Proto.buildFsm = function(module)
+{
+ var fsm = module.fsm;
+
+ /*
+ * State: Idle
+ *
+ * This is a null state to replace the one that loads the API viewer. The
+ * API viewer does not use the finite state machine.
+ */
+ var state = new qx.util.fsm.State(
+ "State_Idle",
+ {
+ "events" :
+ {
+ // We need at least one event listed due to FSM requirements
+ "appear" :
+ {
+ "swat.module.canvas" :
+ "Transition_Idle_to_Idle_via_appear"
+ }
+ }
+ });
+
+ // Replace the initial Idle state with this one
+ fsm.replaceState(state, true);
+
+ /*
+ * Transition: Idle to Idle
+ *
+ * Cause: "appear" on canvas
+ *
+ * Action:
+ * None.
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_Idle_via_appear",
+ {
+ "nextState" :
+ "State_Idle"
+ });
+ state.addTransition(trans);
+
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js b/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js
new file mode 100644
index 0000000000..9362ef7687
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js
@@ -0,0 +1,383 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat LDB Browser class finite state machine
+ */
+qx.OO.defineClass("swat.module.ldbbrowse.Fsm", swat.module.AbstractModuleFsm,
+function()
+{
+ swat.module.AbstractModuleFsm.call(this);
+});
+
+
+qx.Proto.buildFsm = function(module)
+{
+ var fsm = module.fsm;
+
+ /*
+ * State: Idle
+ *
+ * Actions upon entry
+ * - if returning from RPC, display the result
+ *
+ * Transition on:
+ * "execute" on find button
+ * "treeopenwhileempty" on tree
+ * "changeselection" on tree
+ */
+ var state = new qx.util.fsm.State(
+ "State_Idle",
+ {
+ "onentry" :
+ function(fsm, state)
+ {
+ // Did we just return from an RPC request?
+ if (fsm.getPreviousState() == "State_AwaitRpcResult")
+ {
+ // Yup. Display the result. We need to get the request object
+ var request = fsm.getObject("swat.module.fsmUtils.request");
+
+ // We don't need the request object to be saved any more
+ fsm.removeObject("swat.module.fsmUtils.request");
+
+ // Display the result
+ var gui = swat.module.ldbbrowse.Gui.getInstance();
+ gui.displayData(module, request);
+
+ // Dispose of the request
+ request.dispose();
+ request = null;
+ }
+ },
+
+ "events" :
+ {
+ // If the find button is activated, issue a find request
+ "execute" :
+ {
+ "find" :
+ "Transition_Idle_to_AwaitRpcResult_via_find"
+ },
+
+ // If a previously unexpanded tree node is expanded, issue a request
+ // to retrieve its contents.
+ "treeOpenWhileEmpty":
+ {
+ "tree" :
+ "Transition_Idle_to_AwaitRpcResult_via_tree_open"
+ },
+
+ // If the selection changes, issue a request to retrieve contents to
+ // populate the attribute/value table.
+ "changeSelection":
+ {
+ "tree:manager" :
+ "Transition_Idle_to_AwaitRpcResult_via_tree_selection_changed",
+
+ "dbName":
+ "Transition_Idle_to_AwaitRpcResult_via_db_changed"
+ }
+ }
+ });
+
+ // Replace the initial Idle state with this one
+ fsm.replaceState(state, true);
+
+ /*
+ * Transition: Idle to AwaitRpcResult
+ *
+ * Cause: "execute" on find button
+ *
+ * Action:
+ * Issue a search request
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_AwaitRpcResult_via_find",
+ {
+ "nextState" :
+ "State_AwaitRpcResult",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ // Obtain the RPC object
+ var rpc = fsm.getObject("swat.module.rpc");
+
+ // Get our module descriptor
+ var module = fsm.getObject("swat.module.module");
+
+ // Retrieve the database handle
+ var dbHandle = module.dbHandle;
+
+ // Retrieve the search expression
+ var searchExpr = fsm.getObject("searchExpr").getValue();
+
+ // Retrieve the base DN
+ var baseDN = fsm.getObject("baseDN").getValue();
+
+ // Retrieve the selected scope
+ var scope = fsm.getObject("scope").getSelected().getValue();
+
+ // We want all attributes
+ var attributes = [ "*" ];
+
+ rpc.setServiceName("samba.ldb");
+ var request = rpc.callAsyncListeners(true, // coalesce failure events
+ "search",
+ dbHandle,
+ searchExpr,
+ baseDN,
+ scope,
+ attributes);
+
+ // When we get the result, we'll need to know what type of request
+ // we made.
+ request.setUserData("requestType", "find");
+
+ // Save the request object
+ fsm.addObject("swat.module.fsmUtils.request", request);
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: Idle to AwaitRpcResult
+ *
+ * Cause: "treeOpenWhileEmpty" on tree
+ *
+ * Action:
+ * Issue a search request
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_AwaitRpcResult_via_tree_open",
+ {
+ "nextState" :
+ "State_AwaitRpcResult",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ var parent = event.getData();
+ var hierarchy = parent.getHierarchy(new Array());
+
+ parent.debug("Requesting children...");
+
+ // Strip off the root node
+ hierarchy.shift();
+
+ // Get the tree object
+ var tree = fsm.getObject("tree");
+
+ // 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(",");
+ }
+
+ // Build the search expression
+ var searchExpr = "(objectclass=*)";
+
+ // Obtain the RPC object
+ var rpc = fsm.getObject("swat.module.rpc");
+
+ // Get our module descriptor
+ var module = fsm.getObject("swat.module.module");
+
+ // Retrieve the database handle
+ var dbHandle = module.dbHandle;
+
+ rpc.setServiceName("samba.ldb");
+ var request = rpc.callAsyncListeners(true, // coalesce failure events
+ "search",
+ dbHandle,
+ searchExpr,
+ baseDN,
+ scope,
+ attributes);
+
+ // When we get the result, we'll need to know what type of request
+ // we made.
+ request.setUserData("requestType", "tree_open");
+
+ // We'll also need some of our parameters
+ request.setUserData("parent", parent);
+ request.setUserData("attributes", attributes);
+
+ // Save the request object
+ fsm.addObject("swat.module.fsmUtils.request", request);
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: Idle to AwaitRpcResult
+ *
+ * Cause: "changeSelection" on tree
+ *
+ * Action:
+ * Issue a search request
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_AwaitRpcResult_via_tree_selection_changed",
+ {
+ "nextState" :
+ "State_AwaitRpcResult",
+
+ "predicate" :
+ function(fsm, event)
+ {
+ var element = event.getData()[0];
+ var hierarchy = element.getHierarchy(new Array());
+
+ // Strip off the root node
+ hierarchy.shift();
+
+ // Get the tree object
+ var tree = fsm.getObject("tree");
+
+ // If element is the root...
+ if (element == tree)
+ {
+ // ... then just clear out the attribute/value table.
+ var tableModel = fsm.getObject("tableModel:browse");
+ tableModel.setData([]);
+ return null; // don't search additional transitionis
+ }
+
+ return true;
+ },
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ var element = event.getData()[0];
+ var hierarchy = element.getHierarchy(new Array());
+
+ // Strip off the root node
+ hierarchy.shift();
+
+ // Get the tree object
+ var tree = fsm.getObject("tree");
+
+ // Determine the children. Differs depending on root or otherwise
+ var attributes;
+ var scope;
+ var baseDN;
+
+ // 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(",");
+
+ // Build the search expression
+ var searchExpr = "(objectclass=*)";
+
+ // Obtain the RPC object
+ var rpc = fsm.getObject("swat.module.rpc");
+
+ // Get our module descriptor
+ var module = fsm.getObject("swat.module.module");
+
+ // Retrieve the database handle
+ var dbHandle = module.dbHandle;
+
+ rpc.setServiceName("samba.ldb");
+ var request = rpc.callAsyncListeners(true, // coalesce failure events
+ "search",
+ dbHandle,
+ searchExpr,
+ baseDN,
+ scope,
+ attributes);
+
+ // When we get the result, we'll need to know what type of request
+ // we made.
+ request.setUserData("requestType", "tree_selection_changed");
+
+ // Save the request object
+ fsm.addObject("swat.module.fsmUtils.request", request);
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: Idle to AwaitRpcResult
+ *
+ * Cause: "changeSelection" on dbName
+ *
+ * Action:
+ * Issue a connect request
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_AwaitRpcResult_via_db_changed",
+ {
+ "nextState" :
+ "State_AwaitRpcResult",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ // Obtain the RPC object
+ var rpc = fsm.getObject("swat.module.rpc");
+
+ // Obtain the name of the database to be connected to
+ var dbName = fsm.getObject("dbName").getValue();
+
+ rpc.setServiceName("samba.ldb");
+ var request = rpc.callAsyncListeners(true, // coalesce failure events
+ "connect",
+ dbName);
+
+ // When we get the result, we'll need to know what type of request
+ // we made.
+ request.setUserData("requestType", "database_name_changed");
+
+ // Save the request object
+ fsm.addObject("swat.module.fsmUtils.request", request);
+ }
+ });
+ state.addTransition(trans);
+
+ // Add the AwaitRpcResult state and all of its transitions
+ this.addAwaitRpcResultState(module);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/ldbbrowse/Gui.js b/webapps/swat/source/class/swat/module/ldbbrowse/Gui.js
new file mode 100644
index 0000000000..9e86be25e9
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/ldbbrowse/Gui.js
@@ -0,0 +1,632 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat LDB Browser class graphical user interface
+ */
+qx.OO.defineClass("swat.module.ldbbrowse.Gui", qx.core.Object,
+function()
+{
+ qx.core.Object.call(this);
+});
+
+
+/**
+ * Build the raw graphical user interface.
+ */
+qx.Proto.buildGui = function(module)
+{
+ var o;
+ var fsm = module.fsm;
+
+ // We need a horizontal box layout for the database name
+ var hlayout = new qx.ui.layout.HorizontalBoxLayout();
+ hlayout.set({
+ top: 20,
+ left: 20,
+ right: 20,
+ height: 30
+ });
+
+ // Create a label for the database name
+ o = new qx.ui.basic.Atom("Database:");
+ o.setWidth(100);
+ o.setHorizontalChildrenAlign("right");
+
+ // Add the label to the horizontal layout
+ hlayout.add(o);
+
+ // Create a combo box for for the database name
+ o = new qx.ui.form.ComboBox();
+ o.getField().setWidth("100%");
+ o.setEditable(false);
+
+ // Add our global database name (the only option, for now)
+ var item = new qx.ui.form.ListItem(module.dbFile);
+ o.add(item);
+
+ // We want to be notified if the selection changes
+ o.addEventListener("changeSelection", fsm.eventListener, fsm);
+
+ // Save the database name object so we can react to changes
+ fsm.addObject("dbName", o);
+
+ // Add the combo box to the horizontal layout
+ hlayout.add(o);
+
+ // Add the database name selection to the canvas
+ module.canvas.add(hlayout);
+
+ // Create and position the tabview
+ var tabView_ = new qx.ui.pageview.tabview.TabView;
+ tabView_.set({
+ top: 60,
+ left: 20,
+ right: 20,
+ bottom: 20
+ });
+
+ // Create each of the tabs
+ var tabView_Search =
+ new qx.ui.pageview.tabview.Button("Search");
+ var tabView_Browse =
+ new qx.ui.pageview.tabview.Button("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.Page(tabView_Search);
+ var tabViewPage_Browse =
+ new qx.ui.pageview.tabview.Page(tabView_Browse);
+
+ // Build the search page
+ this._buildPageSearch(module, tabViewPage_Search);
+
+ // Build the browse page
+ this._buildPageBrowse(module, tabViewPage_Browse);
+
+ // Add the pages to the tabview
+ tabView_.getPane().add(tabViewPage_Search, tabViewPage_Browse);
+
+ // Add the tabview to our canvas
+ module.canvas.add(tabView_);
+};
+
+
+/**
+ * Populate the graphical user interface with the specified data
+ *
+ * @param module {swat.module.Module}
+ * The module descriptor for the module.
+ *
+ * @result {Object}
+ * The result returned by SAMBA to our request. We display the data
+ * provided by this result.
+ */
+qx.Proto.displayData = function(module, request)
+{
+ var gui = module.gui;
+ var fsm = module.fsm;
+ var result = request.getUserData("result")
+ var requestType = request.getUserData("requestType");
+
+ // Did the request fail?
+ if (result.type == "failed")
+ {
+ // Yup. We're not going to do anything particularly elegant...
+ alert("Async(" + result.id + ") exception: " + result.data);
+ return;
+ }
+
+ // Dispatch to the appropriate handler, depending on the request type
+ switch(requestType)
+ {
+ case "find":
+ this._displayFindResults(module, request);
+ break;
+
+ case "tree_open":
+ this._displayTreeOpenResults(module, request);
+ break;
+
+ case "tree_selection_changed":
+ this._displayTreeSelectionChangedResults(module, request);
+ break;
+
+ case "database_name_changed":
+ this._clearAllFields(module, request);
+ break;
+
+ default:
+ throw new Error("Unexpected request type: " + requestType);
+ }
+
+ // 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();
+};
+
+
+qx.Proto._setAppearances = function()
+{
+ // 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;
+ }
+};
+
+
+qx.Proto._buildPageSearch = function(module, page)
+{
+ var fsm = module.fsm;
+
+ // 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);
+ fsm.addObject("searchExpr", search);
+
+ // 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);
+ fsm.addObject("baseDN", base);
+
+ // 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
+ ]);
+ fsm.addObject("scope", scope);
+
+ // 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');
+ find.setWidth(100);
+ find.addEventListener("execute", fsm.eventListener, fsm);
+
+ // We'll be receiving events on the find object, so save its friendly name
+ fsm.addObject("find", find, "swat.module.fsmUtils.disable_during_rpc");
+
+ hlayout.add(find);
+
+ // Add the Find button line to the vertical layout
+ vlayout.add(hlayout);
+
+ // 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);
+ fsm.addObject("tableModel:search", tableModel);
+
+ // Create a table
+ var table = new qx.ui.table.Table(tableModel);
+ table.set({
+ top: 80,
+ left: 0,
+ right: 0,
+ bottom: 10,
+ statusBarVisible: false,
+ columnVisibilityButtonVisible: false
+ });
+ table.setColumnWidth(0, 300);
+ table.setColumnWidth(1, 180);
+ table.setColumnWidth(2, 240);
+ table.setMetaColumnCounts([ 1, -1 ]);// h-scroll attribute and value together
+ fsm.addObject("table:search", table);
+
+ page.add(table);
+};
+
+qx.Proto._buildPageBrowse = function(module, page)
+{
+ var fsm = module.fsm;
+
+ // 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 trsInstance = qx.ui.treefullcontrol.TreeRowStructure.getInstance();
+ var trs = trsInstance.standard(module.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,
+ alwaysShowPlusMinusSymbol: true
+ });
+
+ // All subtrees will use this root node's event listeners. Create an event
+ // listener for an open while empty.
+ tree.addEventListener("treeOpenWhileEmpty", fsm.eventListener, fsm);
+
+ // 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("changeSelection",
+ fsm.eventListener,
+ fsm);
+
+ // We'll be receiving events on the tree object, so save its friendly name
+ fsm.addObject("tree", tree);
+ fsm.addObject("tree:manager", tree.getManager());
+
+ // 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);
+ fsm.addObject("tableModel:browse", tableModel);
+
+ // Create a table
+ var table = new qx.ui.table.Table(tableModel);
+ table.set({
+ top: 10,
+ left: 0,
+ right: 0,
+ bottom: 10,
+ statusBarVisible: false,
+ columnVisibilityButtonVisible: false
+ });
+ table.setColumnWidth(0, 200);
+ table.setColumnWidth(1, 440);
+ table.setMetaColumnCounts([1, -1]);
+ fsm.addObject("table:browse", table);
+
+ // Add the table to the bottom portion of the splitpane
+ splitpane.addBottom(table);
+
+ // Add the first splitpane to the page
+ page.add(splitpane);
+};
+
+
+qx.Proto._displayFindResults = function(module, request)
+{
+ var rowData = [];
+ var fsm = module.fsm;
+
+ // Track the maximum length of the attribute values
+ var maxLen = 0;
+
+ // Obtain the result object
+ result = request.getUserData("result").data;
+
+ 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]
+ ] );
+ }
+ }
+
+ // Obtain the table and tableModel objects
+ var table = fsm.getObject("table:search");
+ var tableModel = fsm.getObject("tableModel:search");
+
+ // 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.");
+ }
+};
+
+
+qx.Proto._displayTreeOpenResults = function(module, request)
+{
+ var t;
+ var trs;
+ var child;
+
+ // Obtain the result object
+ var result = request.getUserData("result").data;
+
+ // We also need some of the original parameters passed to the request
+ var parent = request.getUserData("parent");
+ var attributes = request.getUserData("attributes");
+
+ // Any children?
+ if (! result || result["length"] == 0)
+ {
+ // Nope. Allow parent's expand/contract button to be removed
+ parent.setAlwaysShowPlusMinusSymbol(false);
+ return;
+ }
+
+ for (var i = 0; i < result.length; i++)
+ {
+ var name;
+
+ child = result[i];
+
+ // Determine name for new tree row. If first level, use entire
+ // DN. Otherwise, strip off first additional component.
+ if (attributes == "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);
+ t.setAlwaysShowPlusMinusSymbol(true);
+
+ // Add this row to its parent
+ parent.add(t);
+ }
+};
+
+
+qx.Proto._displayTreeSelectionChangedResults = function(module, request)
+{
+ var fsm = module.fsm;
+
+ // Obtain the result object
+ var result = request.getUserData("result").data;
+
+ // 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] ]);
+ }
+ }
+
+ // Obtain the table and tableModel objects
+ var table = fsm.getObject("table:browse");
+ var tableModel = fsm.getObject("tableModel:browse");
+
+ // Adjust the width of the value column based on maxLen
+ table.setColumnWidth(1, maxLen * 7);
+
+ // Add the dataset to the table
+ tableModel.setData(rowData);
+};
+
+
+qx.Proto._clearAllFields = function(module, request)
+{
+ // Obtain the result object
+ var result = request.getUserData("result").data;
+
+ // Retrieve the database handle
+ module.dbHandle = result;
+
+ // In the future, when we support more than one database, we'll want to
+ // clear all fields here. For now, there's no need.
+};
+
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/ldbbrowse/LdbBrowse.js b/webapps/swat/source/class/swat/module/ldbbrowse/LdbBrowse.js
new file mode 100644
index 0000000000..45a4c48930
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/ldbbrowse/LdbBrowse.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat LDB Browser class
+ */
+qx.OO.defineClass("swat.module.ldbbrowse.LdbBrowse",
+ swat.module.AbstractModule,
+function()
+{
+ swat.module.AbstractModule.call(this);
+});
+
+
+/**
+ * Create the module's finite state machine and graphical user interface.
+ *
+ * This function is called the first time a module is actually selected to
+ * appear. Creation of the module's actual FSM and GUI have been deferred
+ * until they were actually needed (now) so we need to create them.
+ *
+ * @param module {swat.module.Module}
+ * The module descriptor for the module.
+ */
+qx.Proto.initialAppear = function(module)
+{
+ // Initial database to open
+ module.dbFile = "sam.ldb";
+
+ // Replace the existing (temporary) finite state machine with the real one
+ swat.module.ldbbrowse.Fsm.getInstance().buildFsm(module);
+
+ // Create the real gui
+ swat.module.ldbbrowse.Gui.getInstance().buildGui(module);
+
+ // Force the global database to be opened
+ var dbName = module.fsm.getObject("dbName");
+ dbName.setSelected(dbName.getList().getFirstChild());
+ dbName.dispatchEvent(new qx.event.type.Event("changeSelection"), true);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/statistics/Fsm.js b/webapps/swat/source/class/swat/module/statistics/Fsm.js
new file mode 100644
index 0000000000..771044172e
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/statistics/Fsm.js
@@ -0,0 +1,209 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class finite state machine
+ */
+qx.OO.defineClass("swat.module.statistics.Fsm", swat.module.AbstractModuleFsm,
+function()
+{
+ swat.module.AbstractModuleFsm.call(this);
+});
+
+
+qx.Class._startTimer = function(fsm)
+{
+ // Create a timer instance to expire in a few seconds
+ var timer = new qx.client.Timer(5000);
+ timer.addEventListener("interval", fsm.eventListener, fsm);
+ fsm.addObject("timer", timer);
+ timer.start();
+};
+
+
+qx.Class._stopTimer = function(fsm)
+{
+ // ... then stop the timer. Get the timer object.
+ var timer = fsm.getObject("timer");
+
+ // If it still exists...
+ if (timer)
+ {
+ // ... then dispose of it.
+ timer.dispose();
+ fsm.removeObject("timer");
+ }
+};
+
+
+qx.Proto.buildFsm = function(module)
+{
+ var fsm = module.fsm;
+ var thisClass = this;
+
+ /*
+ * State: Idle
+ *
+ * Actions upon entry
+ * - if returning from RPC, display the result
+ * - start an interval timer to request statistics again in a while
+ *
+ * Transition on:
+ * "interval" on interval_timer
+ */
+ var state = new qx.util.fsm.State(
+ "State_Idle",
+ {
+ "onentry" :
+ function(fsm, state)
+ {
+ // Did we just return from an RPC request?
+ if (fsm.getPreviousState() == "State_AwaitRpcResult")
+ {
+ // Yup. Display the result. We need to get the request object
+ var request = fsm.getObject("swat.module.fsmUtils.request");
+
+ // We don't need the request object to be saved any more
+ fsm.removeObject("swat.module.fsmUtils.request");
+
+ // Display the result
+ var gui = swat.module.statistics.Gui.getInstance();
+ gui.displayData(module, request.getUserData("result"));
+
+ // Dispose of the request
+ request.dispose();
+ request = null;
+
+ // Restart the timer.
+ swat.module.statistics.Fsm._startTimer(fsm);
+ }
+ },
+
+ "onexit" :
+ function(fsm, state)
+ {
+ // If we're not coming right back into this state...
+ if (fsm.getNextState() != "State_Idle")
+ {
+ // ... then stop the timer.
+ swat.module.statistics.Fsm._stopTimer(fsm);
+ }
+ },
+
+ "events" :
+ {
+ // If the timer expires, send a new statistics request
+ "interval" :
+ {
+ "timer" :
+ "Transition_Idle_to_AwaitRpcResult_via_request_statistics"
+ },
+
+ // When we get an appear event, start our timer
+ "appear" :
+ {
+ "swat.module.canvas" :
+ "Transition_Idle_to_Idle_via_appear"
+ },
+
+ // When we get a disappear event, stop our timer
+ "disappear" :
+ {
+ "swat.module.canvas" :
+ "Transition_Idle_to_Idle_via_disappear"
+ }
+ }
+ });
+
+ // Replace the initial Idle state with this one
+ fsm.replaceState(state, true);
+
+ /*
+ * Transition: Idle to AwaitRpcResult
+ *
+ * Cause: "interval" on timer
+ *
+ * Action:
+ * Issue a Get Statistics request
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_AwaitRpcResult_via_request_statistics",
+ {
+ "nextState" :
+ "State_AwaitRpcResult",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ var rpc = fsm.getObject("swat.module.rpc");
+
+ rpc.setServiceName("samba.management");
+ var request = rpc.callAsyncListeners(true, // coalesce failure events
+ "get_statistics",
+ true,
+ true);
+ // Make the request object available to the AwaitRpcResult state
+ fsm.addObject("swat.module.fsmUtils.request", request);
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: Idle to Idle
+ *
+ * Cause: "appear" on canvas
+ *
+ * Action:
+ * Start our timer
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_Idle_via_appear",
+ {
+ "nextState" :
+ "State_Idle",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ swat.module.statistics.Fsm._startTimer(fsm);
+ }
+ });
+ state.addTransition(trans);
+
+ /*
+ * Transition: Idle to Idle
+ *
+ * Cause: "disappear" on canvas
+ *
+ * Action:
+ * Stop our timer
+ */
+ var trans = new qx.util.fsm.Transition(
+ "Transition_Idle_to_Idle_via_disappear",
+ {
+ "nextState" :
+ "State_Idle",
+
+ "ontransition" :
+ function(fsm, event)
+ {
+ swat.module.statistics.Fsm._stopTimer(fsm);
+ }
+ });
+ state.addTransition(trans);
+
+ // Add the AwaitRpcResult state and all of its transitions
+ this.addAwaitRpcResultState(module);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/statistics/Gui.js b/webapps/swat/source/class/swat/module/statistics/Gui.js
new file mode 100644
index 0000000000..777caa7328
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/statistics/Gui.js
@@ -0,0 +1,464 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class graphical user interface
+ */
+qx.OO.defineClass("swat.module.statistics.Gui", qx.core.Object,
+function()
+{
+ qx.core.Object.call(this);
+});
+
+
+/*
+ * The result of our request for statistics is in this form:
+ *
+ * rpc: Object
+ * status: INACTIVE
+ * smb: Object
+ * tcons: Array
+ * 0: Object
+ * share_name: tmp
+ * last_use_time: 1167186771
+ * client_ip: 127.0.0.1
+ * tid: 10928
+ * connect_time: 1167186757
+ * connections: 1
+ * sessions: Array
+ * 0: Object
+ * auth_time: 1167186757
+ * vuid: 24588
+ * last_use_time: 1167186771
+ * client_ip: 127.0.0.1
+ * connect_time: 1167186757
+ * account_name: Administrator
+ * domain_name: WORKGROUP
+ * status: RUNNING
+ * ldap: Object
+ * status: INACTIVE
+ * wins: Object
+ * status: DISABLED
+ * nbt: Object
+ * status: RUNNING
+ * statistics: Object
+ * total_received: 32
+ * total_sent: 4
+ * query_count: 0
+ * release_count: 0
+ * register_count: 0
+ * kdc: Object
+ * status: INACTIVE
+ * cldap: Object
+ * status: RUNNING
+ */
+
+/**
+ * Build the raw graphical user interface.
+ */
+qx.Proto.buildGui = function(module)
+{
+ var o;
+ var fsm = module.fsm;
+ var canvas = module.canvas;
+
+ canvas.setOverflow("auto");
+
+ // Create a gui object where we'll put each widget handle that has varying
+ // data to be displayed.
+ module.gui = { };
+
+ var addCaptionedText = function(caption, dest)
+ {
+ // Add a row to the destination grid
+ dest.addRow();
+ var row = dest.getRowCount() - 1;
+ dest.setRowHeight(row, 16);
+
+ // Add the caption
+ o = new qx.ui.basic.Label(caption);
+ dest.add(o, 0, row);
+
+ // Add the text field that will contain varying data
+ o = new qx.ui.basic.Label("");
+ dest.add(o, 1, row);
+
+ // Give 'em the varying data label
+ return o;
+ };
+
+ var addGroup = function(legend, top, height, width, left, right, dest)
+ {
+ // Add a groupbox
+ var group = new qx.ui.groupbox.GroupBox(legend);
+ group.setTop(top);
+ if (left >= 0)
+ {
+ group.setLeft(left);
+ }
+ if (right >= 0)
+ {
+ group.setRight(right);
+ }
+ if (height >= 0)
+ {
+ group.setHeight(height);
+ }
+ if (typeof(width) == "string" || width >= 0)
+ {
+ group.setWidth(width);
+ }
+ group.setBackgroundColor("white");
+ group.getLegendObject().setBackgroundColor("white");
+
+ var grid = new qx.ui.layout.GridLayout();
+ grid.setLocation(0, 0);
+ grid.setDimension("100%", "100%");
+ grid.setPadding(0, 0);
+ grid.setRowCount(0);
+ grid.setColumnCount(2);
+ grid.setColumnWidth(0, 100);
+ grid.setColumnWidth(1, 200);
+
+ group.add(grid);
+ dest.add(group);
+
+ return grid;
+ };
+
+ // Add the RPC Service group box and its status
+ var group = addGroup("RPC Service", 40, 60, "46%", 20, -1, canvas);
+ module.gui.rpc =
+ {
+ status : addCaptionedText("Status:", group)
+ };
+
+ // Add the KDC Service group box and its status
+ var group = addGroup("KDC Service", 40, 60, "46%", -1, 20, canvas);
+ module.gui.kdc =
+ {
+ status : addCaptionedText("Status:", group)
+ };
+
+ // Add the LDAP Service group box and its status
+ var group = addGroup("LDAP Service", 120, 60, "46%", 20, -1, canvas);
+ module.gui.ldap =
+ {
+ status : addCaptionedText("Status:", group)
+ };
+
+ // Add the CLDAP Service group box and its status
+ var group = addGroup("CLDAP Service", 120, 60, "46%", -1, 20, canvas);
+ module.gui.cldap =
+ {
+ status : addCaptionedText("Status:", group)
+ };
+
+ // Add the WINS Service group box and its status
+ var group = addGroup("WINS Service", 200, 60, "46%", 20, -1, canvas);
+ module.gui.wins =
+ {
+ status : addCaptionedText("Status:", group)
+ };
+
+ // Add the NBT Service group box and its status, and the statistics
+ var group = addGroup("NBT Service", 200, 140, "46%", -1, 20, canvas);
+ module.gui.nbt =
+ {
+ status : addCaptionedText("Status:", group),
+ total_received : addCaptionedText("Total received:", group),
+ total_sent : addCaptionedText("Total sent:", group),
+ query_count : addCaptionedText("Query count:", group),
+ release_count : addCaptionedText("Release count:", group),
+ register_count : addCaptionedText("Register count:", group)
+ };
+
+ // Add the SMB Service group box (sans grid) and its status
+ var group = new qx.ui.groupbox.GroupBox("SMB Service");
+ group.set({
+ top: 360,
+ height: 400,
+ left: 20,
+ right: 20
+ });
+ group.setBackgroundColor("white");
+ group.getLegendObject().setBackgroundColor("white");
+
+ // Create the Status block
+ o = new qx.ui.basic.Label("Status:");
+ o.set({
+ top : 0,
+ left : 0,
+ width : 100
+ });
+ group.add(o);
+
+ o = new qx.ui.basic.Label("");
+ o.set({
+ top : 0,
+ left : 100,
+ width : 200
+ });
+ group.add(o);
+
+ // Add the status and create the table models for sessions and connections
+ module.gui.smb =
+ {
+ status : o,
+ sessions : new qx.ui.table.SimpleTableModel(),
+ tcons : new qx.ui.table.SimpleTableModel()
+ };
+
+ // Begin the Sessions section
+ o = new qx.ui.basic.Label("Sessions");
+ o.set({
+ top : 20,
+ left : 20
+ });
+ group.add(o);
+
+ // Set column labels
+ var tableModel = module.gui.smb.sessions;
+ tableModel.setColumns([
+ "User",
+ "Client",
+ "Connected at",
+ "Authenticated at",
+ "Last used at",
+ "VUID"
+ ]);
+ tableModel.setData([ ]);
+
+ // Create the table for sessions
+ var table = new qx.ui.table.Table(tableModel);
+ table.set({
+ top : 40,
+ left : 20,
+ right : 20,
+ height : 160
+ });
+ table.setMetaColumnCounts([1, -1]);
+ table.setStatusBarVisible(false);
+ table.setColumnVisibilityButtonVisible(false);
+ table.setColumnWidth(0, 260);
+ table.setColumnWidth(1, 80);
+ table.setColumnWidth(2, 120);
+ table.setColumnWidth(3, 120);
+ table.setColumnWidth(4, 120);
+ table.setColumnWidth(5, 60);
+
+ // Add the table to the groupbox
+ group.add(table);
+ canvas.add(group);
+
+ // Begin the Connections section
+ o = new qx.ui.basic.Label("Connections");
+ o.set({
+ top : 220,
+ left : 20
+ });
+ group.add(o);
+
+ // Create the table model for tcons
+ var tableModel = module.gui.smb.tcons;
+ tableModel.setColumns([
+ "Share",
+ "Client",
+ "Connected at",
+ "Last used at",
+ "TID"
+ ]);
+ tableModel.setData([ ]);
+
+ // Create the table for sessions
+ var table = new qx.ui.table.Table(tableModel);
+ table.set({
+ top : 240,
+ left : 20,
+ right : 20,
+ bottom : 20
+ });
+ table.setMetaColumnCounts([1, -1]);
+ table.setStatusBarVisible(false);
+ table.setColumnVisibilityButtonVisible(false);
+ table.setColumnWidth(0, 260);
+ table.setColumnWidth(1, 80);
+ table.setColumnWidth(2, 120);
+ table.setColumnWidth(3, 120);
+ table.setColumnWidth(4, 60);
+
+ // Add the table to the groupbox
+ group.add(table);
+ canvas.add(group);
+
+};
+
+
+/**
+ * Populate the graphical user interface with the specified data
+ *
+ * @param module {swat.module.Module}
+ * The module descriptor for the module.
+ *
+ * @result {Object}
+ * The result returned by SAMBA to our request for statistics. We display
+ * the data provided by this result.
+ */
+qx.Proto.displayData = function(module, result)
+{
+ var gui = module.gui;
+
+ if (result.type == "failed")
+ {
+ // Have we already put up the FAILED message?
+ if (gui.failed)
+ {
+ // Yup.
+ gui.failed.setDisplay(true);
+ return;
+ }
+
+ // Create a semi-transparent layover o which to display a failure message
+ gui.failed = new qx.ui.layout.CanvasLayout();
+ gui.failed.set({
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0
+ });
+ gui.failed.setBackgroundColor("white");
+ gui.failed.setDisplay(true); // initially displayed
+ gui.failed.setOpacity(0.7); // semi-transparent
+
+ // Add the failure message
+ var style =
+ "color: red;" +
+ "font-size: large;" +
+ "font-weight: bold;";
+ var o = new qx.ui.basic.Label("<span style='" + style + "'>" +
+ "Communication with SAMBA failed!",
+ "</span>");
+ o.set({
+ top : 0,
+ left : 20
+ });
+ gui.failed.add(o);
+
+ // Add the failed layover to the canvas
+ module.canvas.add(gui.failed);
+
+ return;
+ }
+
+ // Successful RPC request.
+ // If the failure message was displayed, we no longer need it.
+ if (gui.failed)
+ {
+ gui.failed.setDisplay(false);
+ }
+
+ // Create a function for formatting dates
+ var dateFormat = function(unixepoch)
+ {
+ var d = new Date(unixepoch * 1000);
+ return (d.getFullYear() + "-" +
+ ("0" + (d.getMonth() + 1)).substr(-2) + "-" +
+ ("0" + d.getDate()).substr(-2) + " " +
+ ("0" + d.getHours()).substr(-2) + ":" +
+ ("0" + d.getMinutes()).substr(-2));
+ }
+
+ // Set the status values
+ gui.rpc.status.setHtml(result.data.rpc.status);
+ gui.kdc.status.setHtml(result.data.kdc.status);
+ gui.ldap.status.setHtml(result.data.ldap.status);
+ gui.cldap.status.setHtml(result.data.cldap.status);
+ gui.wins.status.setHtml(result.data.wins.status);
+ gui.nbt.status.setHtml(result.data.nbt.status);
+ gui.smb.status.setHtml(result.data.smb.status);
+
+ // If the NBT service is running...
+ if (result.data.nbt.status == "RUNNING")
+ {
+ // ... then output the statistics
+ gui.nbt.total_received.setHtml(
+ result.data.nbt.statistics.total_received.toString());
+ gui.nbt.total_sent.setHtml(
+ result.data.nbt.statistics.total_sent.toString());
+ gui.nbt.query_count.setHtml(
+ result.data.nbt.statistics.query_count.toString());
+ gui.nbt.release_count.setHtml(
+ result.data.nbt.statistics.release_count.toString());
+ gui.nbt.register_count.setHtml(
+ result.data.nbt.statistics.register_count.toString());
+ }
+ else
+ {
+ // otherwise, clear the statistics fields
+ gui.nbt.total_received.setHtml("");
+ gui.nbt.total_sent.setHtml("");
+ gui.nbt.query_count.setHtml("");
+ gui.nbt.release_count.setHtml("");
+ gui.nbt.register_count.setHtml("");
+ }
+
+ // Initialize data for sessions list
+ var rowData = [];
+
+ // If there are any sessions...
+ if (result.data.smb.sessions instanceof Array)
+ {
+ // ... then for each session...
+ for (var i = 0; i < result.data.smb.sessions.length; i++)
+ {
+ // ... add its info to the table data
+ var sess = result.data.smb.sessions[i];
+ rowData.push([
+ sess.account_name + "/" + sess.domain_name,
+ sess.client_ip,
+ dateFormat(sess.connect_time),
+ dateFormat(sess.auth_time),
+ dateFormat(sess.last_use_time),
+ sess.vuid.toString()
+ ]);
+ }
+ }
+
+ // Whether there were sessions or not, reset the session table data
+ gui.smb.sessions.setData(rowData);
+
+ // Initialize data for tcons list
+ var rowData = [];
+
+ // If there are any tcons...
+ if (result.data.smb.tcons instanceof Array)
+ {
+ // ... then for each tcon...
+ for (var i = 0; i < result.data.smb.tcons.length; i++)
+ {
+ // ... add its info to the table data
+ var conn = result.data.smb.tcons[i];
+ rowData.push([
+ conn.share_name,
+ conn.client_ip,
+ dateFormat(conn.connect_time),
+ dateFormat(conn.last_use_time),
+ conn.tid.toString()
+ ]);
+ }
+ }
+
+ // Whether there were tcons or not, reset the tcon table data
+ gui.smb.tcons.setData(rowData);
+};
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/class/swat/module/statistics/Statistics.js b/webapps/swat/source/class/swat/module/statistics/Statistics.js
new file mode 100644
index 0000000000..1618ab73c5
--- /dev/null
+++ b/webapps/swat/source/class/swat/module/statistics/Statistics.js
@@ -0,0 +1,44 @@
+/*
+ * Copyright:
+ * (C) 2006 by Derrell Lipman
+ * All rights reserved
+ *
+ * License:
+ * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class
+ */
+qx.OO.defineClass("swat.module.statistics.Statistics",
+ swat.module.AbstractModule,
+function()
+{
+ swat.module.AbstractModule.call(this);
+});
+
+
+/**
+ * Create the module's finite state machine and graphical user interface.
+ *
+ * This function is called the first time a module is actually selected to
+ * appear. Creation of the module's actual FSM and GUI have been deferred
+ * until they were actually needed (now) so we need to create them.
+ *
+ * @param module {swat.module.Module}
+ * The module descriptor for the module.
+ */
+qx.Proto.initialAppear = function(module)
+{
+ // Replace the existing (temporary) finite state machine with the real one
+ swat.module.statistics.Fsm.getInstance().buildFsm(module);
+
+ // Create the real gui
+ swat.module.statistics.Gui.getInstance().buildGui(module);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/webapps/swat/source/index.html b/webapps/swat/source/index.html
new file mode 100644
index 0000000000..ea7bcfe871
--- /dev/null
+++ b/webapps/swat/source/index.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Swat</title>
+ <script type="text/javascript" src="script/swat.js"></script>
+ </head>
+ <body>
+ <script type="text/javascript">
+
+ qx.core.Init.getInstance().setApplication(swat.main.Main);
+
+ </script>
+ </body>
+</html>