Archive | July 2006

AJAX Agent, SAPRFC and SU01

It’s not a new topic but it is one that has not been covered very much, at least not together with SAP. Which do I mean? AJAX, one of the buzz words out there right now, so what is it, how do I get it into my application and why would I?

Well I decided the best way for me to describe it was to build on the work of others on SDN.

What I am going to do is take a simple ABAP FM, my trusty PHP connector for SAP and the RFC library and a very basic login.


<?php

// Inlcude Cmehil Functions
include "lib/sap.php";

// Include AJAX Agent
include_once ("lib/agent.php");

// Initialize AJAX Agent
$agent->init();
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>AJAX Agent connection to SAPRFC</title>
</head>

<body>

<script type="text/javascript">
function hide_uservalid(uid) {
document.getElementById(uid).style.display = "none";
}

function call_uservalid(uid) {
document.getElementById(uid).style.display = "block";
agent.call('','GetValid','callback_uservalid', uid);
}

function callback_uservalid(str) {
uid = str.substring(0,str.indexOf(':'));
str = str.substr(str.indexOf(':')+1,str.length);
document.getElementById(uid).innerHTML = str;
}

function call_usersearch() {
str = document.getElementById('q').value;
agent.call('','UserList','callback_usersearch', str);
}

function callback_usersearch(str) {
document.getElementById('div_user').innerHTML = str;
}
</script>

<form onsubmit="call_usersearch();return false;" method="post">
<b><span>Query</span>:</b> <input type="text" name="q" id="q" size="10">
</form>
<hr>
<div id="div_user"></div>

</body>
</html>

As you can see my initial, or “index.php” page is nothing special just a little JavaScript and some HTML and a tiny splash of PHP through in at the top.

Then I have two subfolders, one is “images” the other is “lib”, the “images” subfolder contains two images I snagged from my NetWeaver system:

image image

The “lib” subfolder contains my “saprfc.php” file which comes with the SAPRFC for PHP download along with “agent.php” which comes with AJAX Agent which I decided to use to make life a bit easier for myself. You could also make the connections yourself, like in how Dan describes here in his blog.

The last item under “lib” is my own small collection of PHP functions.


<?php
// saprfc-class-library and custom library su01
require_once ("saprfc.php");

function login($user,$pwd,$host,$sn,$clnt) {
// Create saprfc-instance
$sap = new saprfc(array(
"logindata"=>array(
"ASHOST"=>$host // application server
,"SYSNR"=>$sn // system number
,"CLIENT"=>$clnt // client
,"USER"=>$user // user
,"PASSWD"=>$pwd // password
)
,"show_errors"=>false // let class printout errors
,"debug"=>false)) ; // detailed debugging information
return $sap;
}

function logoff($sap) {
// Logoff/Close saprfc-connection LL/2001-08
$sap->logoff();
}

function UserList($query) {
$listing = "";

// Login into System
$sap = login("bcuser","minisap","localhost","00","000");

// Call-Function
$result=$sap->callFunction("SO_USER_LIST_READ",
array( array("IMPORT","USER_GENERIC_NAME","*"),
array("TABLE","USER_DISPLAY_TAB",array())
));

// Call successfull?
if ($sap->getStatus() == SAPRFC_OK) {
// Yes, print out the Userlist
$i = 0;
$listing .= "<table cellpadding="4" cellspacing="4">";
foreach ($result["USER_DISPLAY_TAB"] as $user) {
if ($query == "*" || $query == "%" || stristr($user["SAPNAM"], $query)) {
$i++;
$listing .= "<tr><td><a href="#""
. " onmouseout="hide_uservalid('".$user["SAPNAM"]."');return false;""
. " onmouseover="call_uservalid('".$user["SAPNAM"]."');return"
. " false;">".$user["SAPNAM"]."</a></td><td"
. " align="center"><div id="".$user["SAPNAM"]."""
. "></div></td></tr>";
}
}
$listing .= "</table>";
} else {
// No, print long Version of last Error
$listing = $sap->printStatus();
}

// Logoff System
logoff($sap);

// Now return the list
return $listing;
}

function GetValid($uid) {
$value = "";

$result = GetUserLogonDetails($uid);
$input = $result["GLTGB"];

// Yes, then get value
if ( strtotime("now") < strtotime($input) or $input == "00000000" ) {
$value = "<img src="images/s_S_OKAY.gif" border="0" alt="".$input."">";
} else {
$value = "<img src="images/s_S_NONO.gif" border="0" alt="".$input."">";
}

return $uid.":".$value;
}

function GetUserLogonDetails($uid) {
$value = "";

// Login into System
$sap = login("bcuser","minisap","localhost","00","000");

$uid = strtoupper($uid);
$result=$sap->callFunction("BAPI_USER_GET_DETAIL",
array( array("IMPORT","USERNAME",$uid),
array("EXPORT","LOGONDATA",array())
));
// Call successfull?
if ($sap->getStatus() == SAPRFC_OK) {
// Yes, then get value
$value = $result["LOGONDATA"];
} else {
// No, print long Version of last Error
$sap->printStatus();
}

// Logoff System
logoff($sap);

return $value;
}

?>

Really that is all there is two it. All of it together is similar to what Piers Harding created using Ruby. Although this is a strictly “read” only version and not so “flashy” as his.

I recommend you check the forum link (see ABAP FM) for some alternatives to the standard FM from SAP for better performance. I tested against a system with 436 users and the response was fine but the idea of grabbing the entire list of users each time you run the query is a bit much.

Ok now we have it, it’s pretty fast and easy but why do I want it? Well in my test system I pulled 436 users, my biggest query based search was 128 users now if you notice in the code I have it set that when you move your mouse over the User ID (onmouseover) it shows me whether they are valid in the system or not, this happens dynamically each time I move the mouse over the User ID. Now what happens in a traditional manner is that I would click on the name, it would show me the status after reloading the page AND the 128 users again remembering what the query was, where I clicked etc. This approach (granted not the best in terms of performance) does all of this without reloading the page. The other option would be of course to pul the list together with the status and just give me all of it at once, this also goes to show that there are a million and one ways to accomplish something – this way offers you the comfort of not reloading your pages for every action you wish to accomplish.

A nice definition and more information can be found on Wikipedia.

AJAX, shorthand for Asynchronous JavaScript and XML, is a Web development technique for creating interactive web applications. The intent is to make web pages feel more responsive by exchanging small amounts of data with the server behind the scenes, so that the entire web page does not have to be reloaded each time the user makes a change. This is meant to increase the web page’s interactivity, speed, and usability.

The following link is where you can actually download the entire demo and save typing it in.

Remember this is a small snippet to show the potential and in no way should this be taken as a productive example for wide use.

OK now with that all out of the way let’s take a look at what’s actually going on, you see things are triggered via a JavaScript method:


function call_usersearch() {
str = document.getElementById('q').value;
agent.call('','UserList','callback_usersearch', str);
}

function callback_usersearch(str) {
document.getElementById('div_user').innerHTML = str;
}

So we have the intital “call” function which pulls in any values we might want to pass to the PHP function which is done by the call to the AJAX Agent. Here we call the “UserList” PHP function which is known because of the inlcude library files. It also knows to pass in the “return function” as well as the parameter that is needed for the PHP function.

Once the PHP function executes all output is captured and returned to the “return function” which then sets the “content” area of our DIV tag with that very output. Thus we are dynamically changing elements on our page with output generated by the PHP functions.

That is actually a pretty low tech explanation but there is so much information out there on the topic I don’t want to bore you too much, unless you want me to then I promise my next blog will go into more depth than this surface level one.