How to access NDS from HTML or ASP, Part 5 How-To Article

How to access NDS from HTML or
ASP, Part 5
How-To Article
NOVELL APPNOTES
Rostislav Letos
Software Engineer
Developer Support
rletos@novell.com
This article, the fifth in our series, explains browsing the DS tree remotely. For
Part 4 of this series, see http://developer.novell.com/research/appnotes/2001/
july/06/a010706.htm.
Contents:
•
Browsing DS Tree Remotely
•
Remote Scripting in ASP
•
Understanding Remote Scripting
•
How this is Done
•
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
A u g u s t
2 0 0 1
1
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.
2
www.novell.com/appnotes
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.
PC Client
WEB server
IE
http://www. . .
Request
ASP engine
ASP 1
+
scripts
ASP
scripting
ly
p
Re
Local
scripting
IE
ASP engine
Remote Scripting
HTML 1
+
scripts
ASP 2
+
scripts
ASP
scripting
Figure 1: 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.
A u g u s t
2 0 0 1
3
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.
Figure 2: Application in an IE browser.
How this is Done
We start with the Browser1.asp which holds the server scripting code:
4
www.novell.com/appnotes
<%@ 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 %>
A u g u s t
2 0 0 1
5
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:
6
www.novell.com/appnotes
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
A u g u s t
2 0 0 1
7
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
Dim
Dim
Dim
Dim
8
ListContainer(operation)
xnode, xchild
shortName, context, objectList()
object, index, co, msgWindow
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)
www.novell.com/appnotes
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"
A u g u s t
2 0 0 1
9
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.
10
www.novell.com/appnotes
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:
A u g u s t
2 0 0 1
11
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.Cont2…ContX”.
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:
12
www.novell.com/appnotes
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:
Figure 3:
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).
A u g u s t
2 0 0 1
13
Figure 4: Attempt to browse
an object.
And second the error, which is returned by DS server (Figure 5).
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.
14
www.novell.com/appnotes
Copyright © 2001 by Novell, Inc. All rights reserved.
No part of this document may be reproduced or transmitted
in any form or by any means, electronic or mechanical,
including photocopying and recording, for any purpose
without the express written permission of Novell.
All product names mentioned are trademarks of
their respective companies or distributors.
A u g u s t
2 0 0 1
15