How to access NDS from HTML or ASP, Part 5
Articles and Tips: article
Software Engineer
Developer Support
rletos@novell.com
01 Aug 2001
This article, the fifth in our series, explains browsing the DS tree remotely. For Part 4 of this series, see July 2001 Appnotes, How to Access NDS from HTML or ASP, Part 4.
- Browsing DS Tree Remotely Browsing DS Tree Remotely
- Remote Scripting in ASP Remote Scripting in ASP
- Understanding Remote Scripting Understanding Remote Scripting
- How this is Done How this is Done
- Conclusion Conclusion
Topics |
directory services, NDS eDirectory, HTML, Active Server Pages (ASP), ActiveX, NWDir |
Products |
NDS eDirectory |
Audience |
developers, administrators |
Level |
beginning |
Prerequisite Skills |
familiarity with NDS |
Operating System |
NetWare 4 or 5, Windows 95/98/NT/2000 |
Tools |
none |
Sample Code |
yes |
Browsing DS Tree Remotely
Is it possible to create an application, which would allow us to remotely browse NDS in expandable TreeView object, by a simple click of the mouse? Although we have client scripting possibilities on the client side and ASP scripting possibilities on the server side, is it enough to create an ASP application similar to this one?
We have learned and already know how to embed TreeView object into an HTML browser window and how to browse the NDS from locally installed NW client. We also know how to embed NWDir control into ASP page and get the information from NWDir to the local browser. But is it enough to create our fully interactive application?
The answer is NO. What we are missing in our environment, is an interaction between the client side and a server side. Imagine -we can ask the server to browse the tree, and we will get the information we require. But either we have to download and put the whole tree structure into the TreeView in one run only, or we can get objects belonging to one container only. Neither of these approaches can help us. The first approach can be time consuming and crash our application due to the lack of memory (on the server or client side). It also does not reflect possible changes in DS tree structure since the whole data is downloaded once into the browser window. On the other hand the later approach does not allow us to modify and display already existing TreeView object because we have to load/reload our HTML page frequently.
We are missing an interaction between the client side scripting and the server side scripting. By default client and server scripts are mutually exclusive, which means that the HTML document must be reloaded each time there is a need for data exchange between the server and the client scripts. We need some kind of interaction, which would allow us to exchange the information as needed, thus keeping the original HTML page active in the client browser (especially the embedded TreeView object).
Remote Scripting in ASP
Microsoft has added to the ASP engine solution for our problem. It is called "Remote scripting capabilities of ASP." Remote scripting is a method, which allows us to work in the client script but call functions and routines, which are part of ASP server scripting. Using remote scripting we can call server scripts as if they were local, but they run on the server. Remote scripting components, if not already installed on your ASP/IIS server, can be freely downloaded from Microsoft's web site.
An overview of an ASP application, when a combination of local, server, and remote scripting are used is presented in Figure 1. You can see that the client browser connects to the Web server and downloads the document HTML1, which contains some scripting code. Using remote scripting possibilities the client browser keeps HTML1 document active thus progressing with the application.
Remote scripting.
To use remote scripting in our ASP sample code we need to understand how remote scripting works so we can run the remote scripting code successfully.
Understanding Remote Scripting
On the client side a Java applet is used as a proxy process, which sends requests and receives replies from the Web server. On the server side another ASP file is used as a dispatcher to dispatch requests from the client to the required functions or procedures. The whole remote scripting functionality is implemented in several library files, which must be available on the Web server. These files are: rs.htm, rs.asp, and rsproxy.class. To use these files within your own ASP project include the file names in your ASP code and call the appropriate methods as needed.
Instead of discussing usage of remote scripting theoretically in details, let's look at the sample code. It consists of two ASP files - Browser.asp and Browser1.asp. Most of the code in Browser.asp is used for the local client side scripting, however, Browser1.asp provides the needed code for server side scripting.
Figure 2 shows how the application looks in an IE browser window. This ASP application, allows us to browse a given DS tree using the TreeView.
Application in an IE browser.
How this is Done
We start with the Browser1.asp which holds the server scripting code:
<%@ LANGUAGE=VBSCRIPT %> <% RSDispatch %> <!--#INCLUDE FILE="_ScriptLibrary/rs.asp"--> <SCRIPT RUNAT=SERVER Language="javascript"> function myServerMethods() { this.ListContainer=Function('n1','return ListContainer(n1)'); } public_description = new myServerMethods(); </SCRIPT> <SCRIPT RUNAT=SERVER LANGUAGE="VBScript"> Function ListContainer(context) Dim NWDir1, objList, i, entry, newcontext, elem set NWDir1=Server.CreateObject("NWDirLib.NWDirCtrl.1") newcontext="" For i=1 to len(context) elem=mid(context,i,1) if elem="." then newcontext=newcontext+"\" else newcontext=newcontext+elem End If Next NWDir1.FullName = "NDS:\\"+newcontext err.number=0 On Error Resume Next If NWDir1.entry.Layout.Container=TRUE Then For each entry in NWDir1.entries If err.number Then objList="Error: " & err.number & ", " & _ err.description Exit For End If objList=objList & entry.shortName & "," Next Else objList="Error: Specified object is not a container !" End If set NWDir1=Nothing ListContainer=objList End Function </SCRIPT>
As you can see the first ASP syntax (after the obligatory <%@ %>) is
<% RSDispatch %>
This is a call to the server side RSDispatch method, which is used to find the appropriate procedure when we call our server scripts from the client scripts. This call must be the first server script, which runs on the page, so it should be put at the top of the ASP page.
The next command
<!--#INCLUDE FILE="_ScriptLibrary/rs.asp"-->
is a server-side INCLUDE statement, which references the rs.asp file. It is important to have a valid path here -otherwise the remote scripting calls will fail. The path used here in our sample is relative to the root directory of Web server (wwwroot).
Next we create a public description of the scripting ASP functions or procedures we want to invoke remotely. This is done with the following code:
<SCRIPT RUNAT=SERVER Language="javascript"> function myServerMethods() { this.ListContainer=Function('n1','return ListContainer(n1)'); } public_description = new myServerMethods(); </SCRIPT>
With this code we create a list of publicly available (which here means available from the remote side) functions or procedures, which are implemented in our ASP server side scripting code. This creation must be done in JavaScript, use the public_description object, and strictly follow the predefined syntax (which slightly differs when describing VBScript functions and JavaScript functions).
The only function we want to remotely use is the function ListContainer(context). This function requires one parameter - the fully distinguished name of the container to read from. We will call this function remotely from the client side, asking the Web server to list the objects in the specified container. Listed objects are returned by this function in one string variable, which holds all object names found, separated by commas.
We first create an instance of NWDir control in our ListContainer() routine:
set NWDir1=Server.CreateObject("NWDirLib.NWDirCtrl.1")
Next we transform the input parameter context into the format which is acceptable by NWDir:
newcontext="" For i=1 to len(context) elem=mid(context,i,1) if elem="." then newcontext=newcontext+"\" else newcontext=newcontext+elem End If Next
We simply trace the context variable and replace each period character we find with the backslash character. Why is this needed? Earlier in this series we learned that using TreeView along with NWDir control we could use a backslash as a path separator for TreeView object. This way both NWDir and TreeView controls accept backslash as a character in full path, which can help quite significantly and also reduces the length of code. Why don't we use this solution in this case? Unfortunately such an approach cannot be used in remote scripting. Sending a string parameter with backslash character would cause an error in this specific case. Whenever the string with backslash characters is passed as a parameter from the client side to the remote scripting procedure, it is understood by the underlying interface as a string path to the file, and the underlying process tries to find the specified file on the Web server. Consequently, this causes an error in our situation because we are not handling files but rather DS fully distinguished names. The only possible workaround is to avoid using backslash characters as a path separator in the passed parameter context. Instead we use period character. Because NWDir requires fully distinguished name with backslash characters, the translation must be processed before passing our context to NWDir control.
After setting NWDir control with required fully distinguished DS name of the container
NWDir1.FullName = "NDS:\\"+newcontext
we can proceed with listing:
err.number=0 On Error Resume Next If NWDir1.entry.Layout.Container=TRUE Then For each entry in NWDir1.entries If err.number Then objList="Error: " & err.number & ", " & err.description Exit For End If objList=objList & entry.shortName & "," Next Else objList="Error: Specified object is not a container !" End If set NWDir1=Nothing ListContainer=objList
There are two important things to notice. First, each object name found under the specified container is simply added to the variable objList, and a comma is used here as a separator. Second, basic error handling must be implemented here. Sometimes a user will try to read an object, which is not a container, and we have to handle this situation accordingly. You also need to be aware that if reading NWDir1.Entries collection fails for any other reason we will have to handle such a situation. In the case of an error we will put a string with "Error:" description into our variable objList and return back.
And here is our browser.asp file, which contains the client scripting code:
<%@ LANGUAGE="VBSCRIPT" %> <!-- FILE: browser.asp --> <HTML> <HEAD> <TITLE>ASP sample code #4 using Novell NWDir ActiveX control</TITLE> </HEAD> <SCRIPT LANGUAGE="VBScript"> Const CREATE_NEW_TREE = 1 Const MODIFY_EXISTING_TREE = 2 Sub Window_OnLoad() Combo1.clear Combo1.Font.Name = "Arial" Combo1.Font.Size = 9 <% set NWSess1=Server.CreateObject("NWSessLib.NWSessCtrl.1") %> <% For each entry in NWSess1.TreeNames %> Combo1.addItem "<% =entry.FullName %>" <% Next %> <% set NWSess1=Nothing %> Combo1.Text = Combo1.List(0) End Sub Sub ListContainer(operation) Dim xnode, xchild Dim shortName, context, objectList() Dim object, index, co, msgWindow Dim nextComma, startPos, strLen, name TreeView1.MousePointer=11 'vbHourGlass If operation = CREATE_NEW_TREE Then TreeView1.Nodes.Clear TreeView1.Sorted = True TreeView1.PathSeparator = "." TreeView1.Font.Name = "Arial" TreeView1.Font.Size =9 shortName = Mid(Combo1.Text, 7) ' strip off NDS:\\ Set xnode = TreeView1.Nodes.Add(, , , shortName) index = xnode.index Else Set xnode = TreeView1.SelectedItem End If If xnode.Children = 0 Then context = xnode.FullPath set co= RSExecute("browser1.asp","ListContainer",context) If co.status<>0 Then set msgWindow=window.open("","","Height=400 width=400 _ left=300 top=300 titlebar=no toolbar=no menubar=no") msgWindow.document.write co.data Else If InStr(1,co.return_value,"Error:") Then msgbox(co.return_value) Else startPos=1 Do nextComma=InStr(startPos,co.return_value,",") If nextComma=0 then Exit Do End If strLen=nextComma-startPos name=Mid(co.return_value,startPos,strLen) Set xchild=TreeView1.Nodes.Add(xnode.index, 4 , ,name) startPos=nextComma+1 Loop While nextComma<>0 End If End If set co=nothing xnode.Expanded = True End If TreeView1.MousePointer=0 'vbDefault End Sub Sub TreeView1_DblClick() ListContainer (MODIFY_EXISTING_TREE) End Sub Sub combo1_Click() if combo1.Text <> "" Then ListContainer(CREATE_NEW_TREE) End If End Sub </SCRIPT> <BODY BGCOLOR=#66CDAA> <OBJECT CLASSID="CLSID:8BD21D30-EC42-11CE-9E0D-00AA006002F3" ID="Combo1" STYLE="HEIGHT: 30px; LEFT: 50px; POSITION: absolute;TOP: 120px; WIDTH: 300px"> </OBJECT> <OBJECT CLASSID="CLSID:0713E8A2-850A-101B-AFC0-4210102A8DA7" ID="TreeView1" STYLE="HEIGHT: 350px; LEFT: 50px; POSITION: absolute; TOP: 200px; WIDTH: 400px"> <PARAM NAME="LineStyle" VALUE="0"> <PARAM NAME="Style" VALUE="6"> <PARAM NAME="Appearance" VALUE="1"> <PARAM NAME="BorderStyle" VALUE="1"> <PARAM NAME="Indentation" VALUE="250"> </OBJECT> <OBJECT CLASSID="CLSID:978C9E23-D4B0-11CE-BF2D-00AA003F40D0" ID="Label1" STYLE="HEIGHT: 15px; LEFT: 50px; POSITION: absolute;TOP: 100px; WIDTH: 160px"> <PARAM NAME="Caption" VALUE="Select connected tree:"> <PARAM NAME="ForeColor" VALUE="0"> <PARAM NAME="BackColor" VALUE="11193702"> </OBJECT> <OBJECT CLASSID="CLSID:978C9E23-D4B0-11CE-BF2D-00AA003F40D0" ID="Label2" STYLE="HEIGHT: 15px; LEFT: 50px; POSITION: absolute;TOP: 180px; WIDTH: 300px"> <PARAM NAME="Caption" VALUE="Browse DS tree by clicking on expandable nodes:"> <PARAM NAME="ForeColor" VALUE="0"> <PARAM NAME="BackColor" VALUE="11193702"> </OBJECT> <SCRIPT LANGUAGE="JavaScript" src="_ScriptLibrary/rs.htm"> </SCRIPT> <SCRIPT LANGUAGE="JavaScript"> RSEnableRemoteScripting("_ScriptLibrary"); </SCRIPT> <FONT face="Arial" size="+1" color="red"> Novell NWDir ActiveX in ASP page <br> Sample code #4 </FONT> </BODY> </HTML>
The code, which enables and activates remote scripting on this page, is in the following lines:
<SCRIPT LANGUAGE="JavaScript" src="_ScriptLibrary/rs.htm"> </SCRIPT> <SCRIPT LANGUAGE="JavaScript"> RSEnableRemoteScripting("_ScriptLibrary"); </SCRIPT>
The first (empty) JavaScript block references to the rs.htm file. The path specified here is the path on the Web server, relative to the Web server's home directory (wwwroot). In case the specified path is not correct or the file does not exist there, our code will fail.
The second JavaScript block calls the method RSEnableRemoteScripting(). This in fact downloads and starts our proxy applet, responsible for sending remote scripting requests to the server and for receiving the replies.
The later JavaScript block should appear in the body portion of our HTML document and it should follow the empty script block, which includes rs.htm.
As you can see we create several instances of COM objects here, using <OBJECT> tags.
Procedure Window_OnLoad(), which is called when page is loaded is responsible for getting DS tree names from the Web server. It uses NWSess.TreeNames collection:
Sub Window_OnLoad() Combo1.clear Combo1.Font.Name = "Arial" Combo1.Font.Size = 9 <% set NWSess1=Server.CreateObject("NWSessLib.NWSessCtrl.1") %> <% For each entry in NWSess1.TreeNames %> Combo1.addItem "<% =entry.FullName %>" <% Next %> <% set NWSess1=Nothing %> Combo1.Text = Combo1.List(0) End Sub
The code in <% %> will be processed on the server and the results - names of the available DS trees - will be transferred to the client and put into combo1 (combobox object) list.
Each time we double click with the mouse on any node in our TreeView1 object, the following event will be fired:
Sub TreeView1_DblClick() ListContainer (MODIFY_EXISTING_TREE) End Sub
Each time we choose a new tree name from the combo1 list with the mouse click, the following event will be fired:
Sub combo1_Click() if combo1.Text <> "" Then ListContainer(CREATE_NEW_TREE) End If End Sub
You can see that the most important procedure on the client side scripting is the procedure ListContainers(). We have already used this procedure in one of previous HTML samples. The code for ListContainers() in this sample is modified to allow remote scripting usage.
First notice that whenever the new tree is requested we set TreeView1.PathSeparator to the period character:
If operation = CREATE_NEW_TREE Then TreeView1.Nodes.Clear TreeView1.Sorted = True TreeView1.PathSeparator = "." TreeView1.Font.Name = "Arial" TreeView1.Font.Size =9
Here is the remote scripting call:
If xnode.Children = 0 Then context = xnode.FullPath set co = RSExecute("browser1.asp","ListContainer",context)
We call RSExecute() method and specify the following parameters:
URL name of the ASP file, which contains requested remote method. It is our browser1.asp file.
Name of the requested method.
Input parameters. In our case we use one parameter and pass fully distinguished DS name there in the form of "DS_TREE.Cont1.Cont2ContX".
This is one of the ways, how to call remote scripting method, synchronously in this case. Sure, this is not the only way, but it is sufficient for our purpose.
After RSExecute() method returns, it instantiates so called Call Object (co in our implementation), which keeps information and results of the remote scripting execution.
Call Object returned from the RSExecute() method contains the following properties:
ID, which holds an ID of the request
Status, holds the value denoting the failure or the success of the operation (-1 = failure, 0 = success).
Message, holds a message describing what happened.
Data, raw data returned by the server (if there is any).
Return_value, an object returned by the server (if there is any).
Context, holds the string identifying the context of the operation.
In case co.status<>0, an error has occurred during the remote scripting execution and the error description is in the co.data property. We open new (small) browser window and write the incoming error message there to inform the user:
If co.status<>0 Then set msgWindow=window.open("","","Height=400 width=400 left=300 top=300 titlebar=no toolbar=no menubar=no") msgWindow.document.write co.data
Here is how the output of this code looks on the client screen:
Output of code.
If co.status=0 you succeeded with your remote scripting call and can investigate co.return_value property for the results. In this implementation co.return_value returns a string with comma separated names of DS objects found under the specified container. Although you may have succeeded with remote scripting code, you might have failed with our NWDir functionality on the Web server. I have already mentioned that when an error appears during our server scripting code execution, we simply fill the output with the "Error:" string and send it back to the client. That's why we have to check whether an NWDir error has appeared or not by testing content of co.return_value:
If InStr(1,co.return_value,"Error:") Then msgbox(co.return_value)
Basically two types of errors can be captured here: First the error which is caused by erroneous attempt to browse an object which is not a container (Figure 4).
Attempt to browse an object.
And second the error, which is returned by DS server (Figure 5).
Error returned by DS server.
In case the return_value property contains valid data, we process it by separating each object name from all others and adding it to the TreeView object:
Else startPos=1 Do nextComma=InStr(startPos,co.return_value,",") If nextComma=0 then Exit Do End If strLen=nextComma-startPos name=Mid(co.return_value,startPos,strLen) Set xchild=TreeView1.Nodes.Add(xnode.index, 4 , ,name) startPos=nextComma+1 Loop While nextComma<>0 End If End If set co=nothing xnode.Expanded = True End If TreeView1.MousePointer=0 'vbDefault End Sub
Conclusion
Now you can create an administration application in which you can manage NDS remotely from any Internet location. In Part 6 we will discuss how to track down possible problems, and the security concerns of an ASP application which accesses NDS.
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.