WSRP PHP Consumer Example Code
From OpenQuote
This page details an example PHP Page demonstrating how to include an OpenQuote product into a website.
Contents |
[edit] index.php PHP Code
<?php /* Document : index - a php example of an OpenQuote portlet consumer This PHP file is a working example of how to integrate OpenQuote products into a website. It is not necesarily demonstrating the most efficient code, or the best way to implement a PHP site, but instead is designed to demonstrate as simply as possible the OpenQuote portlet integration in a way that both developers with both PHP and non-PHP backgrounds can understand and follow. This PHP will allow: 1. registration with an OpenQuote portal server 2. requests for the html for quotation question pages 3. submitition of answers back to the server 4. deregistration form the server For each of the above 4 process a method held in this PHP is called. Each method follows a similar set of steps: 1. get portal server registration handle 2. load an XML template for the specific service call required 3. populate the XML with the registration handle and any other required data 4. create a HTTP connection to the specific portal server service 5. pass the XML to the service 6. read the response XML from the service 7. extract required information from the response The template XML documents (located in the same location as this PHP) used by each of the 4 processes are: 1. RegisterWSRPConsumer.xml 2. RequestWSRPForm.xml 3. SubmitWSRPForm.xml 4. DeregisterWSRPConsumer.xml HTML at the bottom of this PHP allows the user to: 1. specify the OpenQuote server to connect to 2. specify which OpenQuote product they want to quote for 3. restart the quote process for a product (this simply clears a session id) 4. quote for an OpenQuote product (ie displays the portal) 5. deregister the web application from the OpenQuote server (normally automated as part of a web applications clean up code, but for simplicity is a manual button press process in this example PHP) Created on : Dec 02, 2009, 1:53:12 PM Author : matthew tomlinson */ // OpenQuote server urls used by code in this page, to access a different OpenQuote server edit openQuoteServer String $openQuoteServer = "http://ec2-174-129-148-98.compute-1.amazonaws.com:8080"; $registrationServiceUrl = $openQuoteServer . "/portal-wsrp/RegistrationService"; $markupServiceUrl = $openQuoteServer . "/portal-wsrp/MarkupService"; // This is the consumer name used to register with the server (to obtain a registration handle), this name should be unique to this server $consumerName = "TestPHPConsumer"; // This file is used to hold the Consumer Handle, this is cross session variable, and so needs to be held in a file that can be accessed by multiple user sessions $registrationHandleFile = "registrationhandle.txt"; /** * Register the WSRP Consumer and return the consumer handle * This method first checks an application variable (ie cross user session) for a registration handle, * this is held in a file called 'registrationhandle.txt'. * If one is not found, it means this web app has not yet registered itself with the portal server and so * contacts the server to register. The server will return a registration handle which this * method then stores in an application variable. * @return consumer handle */ function getRegistrationHandle() { global $registrationServiceUrl,$consumerName; // get the current registration handle if set $registrationHandle = getPersistedRegistrationHandle(); // if no handle exists, register consumer to get one if (empty($registrationHandle)) { // load consumer registration xml template (on same url as this jsp) $xmlDocument = simplexml_load_file("RegisterWSRPConsumer.xml"); $xmlDocument->registerXPathNamespace("ns1", "urn:oasis:names:tc:wsrp:v1:types"); // apply the consumer name to the consumerregistration xml template $registrationHandleElements = $xmlDocument->xpath("//ns1:register"); $registrationHandleElements[0]->consumerName = $consumerName; // register consumer by posting xml to OpenQuotes portal server registration service $soapReturn = postMessage($registrationServiceUrl, $xmlDocument); $soapReturnXML = $soapReturn['xml']; $soapReturnXML->registerXPathNamespace("ns1", "urn:oasis:names:tc:wsrp:v1:types"); // extract and return consumer handle from the registration service response (XML) <ns1:registerResponse><ns1:registrationHandle> $registrationHandleElements = $soapReturnXML->xpath('//ns1:registerResponse/ns1:registrationHandle/text()'); if ($registrationHandleElements==FALSE || is_null($registrationHandleElements[0]) || empty($registrationHandleElements[0])) { $registrationHandle=""; } else { $registrationHandle=(string) $registrationHandleElements[0]; } // set an application variable to hold the registration handle for later reuse persistRegistrationHandle($registrationHandle); } // return the registration handle to the caller return $registrationHandle; } /** * Deregister the WSRP Consumer * This method deregisters this web application from the OpenQuote portal server if currently registered */ function deregisterConsumer() { global $registrationServiceUrl; // get the registration handle for the web app (set up by getRegistrationHandle) $registrationHandle = getPersistedRegistrationHandle(); // if a registration handle exists, deregister this web application with the OpenQuote portlet server if (!empty($registrationHandle)) { // load consumer deregistration xml template (on same url as this jsp) $xmlDocument = simplexml_load_file("DeregisterWSRPConsumer.xml"); $xmlDocument->registerXPathNamespace("ns1", "urn:oasis:names:tc:wsrp:v1:types"); // apply the registration handle to the consumer deregistration xml template $registrationHandleElements = $xmlDocument->xpath("//ns1:deregister"); $registrationHandleElements[0]->registrationHandle = $registrationHandle; // deregister consumer by posting xml to OpenQuotes portal server registration service $soapReturn = postMessage($registrationServiceUrl, $xmlDocument); $soapReturnXML = $soapReturn['xml']; // finally, for neatness, remove the now redundent application variable holding the old registration handle persistRegistrationHandle(""); } } /** * Request a WSRP form from the producer * This service returns the HTML genterated for the OpenQuote quotation pages * @return quotation portlet HTML */ function requestWSRPForm() { global $markupServiceUrl, $openQuoteServer; // get product to request form for from php session variables, make certain it is not null $productToRequest=$_SESSION['OpenQuoteProduct']; // get sessionID for product from php session variable $sessionID = $_SESSION[$productToRequest . ".ProductSessionID"]; // load request markup xml template (on same url as this php) $xmlDocument = simplexml_load_file("RequestWSRPForm.xml"); $xmlDocument->registerXPathNamespace("ns1", "urn:oasis:names:tc:wsrp:v1:types"); // apply the registration handle to the request markup xml template $registrationHandleElements = $xmlDocument->xpath("//ns1:registrationContext"); $registrationHandleElements[0]->registrationHandle = getRegistrationHandle(); // connect to portal markup service - this is done seperately to give access to request and response header parameters $headers = array(); // set openquote product request property up so correct product HTML is returned $headers['openquote.product']=$productToRequest; // set user session id request property up (if one exists) so correct stage in the quotation process is returned // if one doesn't exist, the quotation process will start from the start, and a new session id will be generated and returned in the response if (!empty($sessionID)) { $headers['Cookie']=$sessionID; } // get the quotation HTML by posting xml to OpenQuotes portal server markup service $soapReturn = postMessage($markupServiceUrl, $xmlDocument, $headers); $soapReturnXML = $soapReturn['xml']; $soapReturnXML->registerXPathNamespace("ns1", "urn:oasis:names:tc:wsrp:v1:types"); // get sessionID from response header if required (ie was not previously set) if (empty($sessionID)) { $soapReturnHeader = $soapReturn['header']; // get the response header field containing the session id $sessionID = $soapReturnHeader['Set-Cookie']; // extract the session id part (the Set-Cookie value looks like this 'JSESSIONID=AF9AA35D62ADBDC7E71DB0A03C04E710; Path=/') $start = stripos($sessionID, "JSESSIONID"); $end = stripos($sessionID, ";", $start); $sessionID = substr($sessionID, $start, $end); // set a session variable to hold the new session id for later reuse $_SESSION[$productToRequest . ".ProductSessionID"] = $sessionID; } // extract the portlet quotation page HTML from the xml returned by the markup service $markupHTMLElements = $soapReturnXML->xpath("//ns1:markupContext/ns1:markupString/text()"); if ($markupHTMLElements==FALSE || is_null($markupHTMLElements[0]) || empty($markupHTMLElements[0])) { $markupHTML=""; } else { $markupHTML=(String) $markupHTMLElements[0]; } // make certain that the form action in the HTML is pointing to this page by overwriting the default portal action // with this page's details this will ensure all submitted question pages will get sent back to this page $markupHTML = str_replace("action='wsrp_rewrite", "action='index.php", $markupHTML); // update all src urls (images, javascript files etc) in the HTML so they point to the full openquote server url $markupHTML = str_replace("src='/quotation/", "src='".$openQuoteServer."/quotation/", $markupHTML) ; // return the quotation portal HTML to the caller return $markupHTML; } /** * Submit a WSRP form to the producer * This service submits all form fields entered in the quotation portal part of the web page to OpenQuote server for validation */ function submitWSRPForm() { global $markupServiceUrl; // get product to request form for from php session variables, make certain it is not null $productToRequest=$_SESSION['OpenQuoteProduct']; // get sessionID for product from php session variable $sessionID = $_SESSION[$productToRequest . ".ProductSessionID"]; // load submit form xml template (on same url as this php) $xmlDocument = simplexml_load_file("SubmitWSRPForm.xml"); $xmlDocument->registerXPathNamespace("ns1", "urn:oasis:names:tc:wsrp:v1:types"); // apply the registration handle to the submit form xml template $registrationHandleElements = $xmlDocument->xpath("//ns1:registrationContext"); $registrationHandleElements[0]->registrationHandle = getRegistrationHandle(); // add the submitted fields from the openquote portal form to the submit form xml template // each field should be added to the interactionParams element in the following structure: // <ns1:interactionParams> // <ns1:formParameters name='fieldName1'><ns1:value>enteredValue1</ns1:value></ns1:formParameters> // <ns1:formParameters name='fieldName1'><ns1:value>enteredValue1</ns1:value></ns1:formParameters> // </ns1:interactionParams> // firstly get each of the names of submitted fields and thier values // Due to the way the _REQUEST array works with form field, it cannot handle // wsrp form fields correctly as the field names contact square brackets. // Instead we use the raw request contents using the file_get_contents command, // and extract each field and value inturn. $cont = file_get_contents("php://input"); $lines = explode("&",$cont); foreach ($lines as $line) { $parts = explode("=",$line,2); $fieldName = urldecode($parts[0]); $fieldValue = urldecode($parts[1]); // if the submitted field value is not null, add it to the submit form xml if (!is_null($fieldValue)) { // get the xml element to which all submitted values are added $interactionParamsElements = $xmlDocument->xpath("//ns1:interactionParams"); // add a formParameter element to the interactionParams element on submit form xml $formParametersElement = $interactionParamsElements[0]->addChild("formParameters"); // add a name attribute equal to the field name to the new formParameter element $formParametersElement->addAttribute('name',$fieldName); // add a value element formParameter element // set the value element with a text content equal to the submitted field value $formParametersElement->addChild("value",$fieldValue); } } // set openquote product request property up so correct product HTML is returned $headers = array(); $headers['openquote.product']=$productToRequest; // set user session id request property up (if one exists) so correct stage in the quotation process is returned // if one doesn't exist, the quotation process will start from the start, and a new session id will be generated and returned in the response if (!empty($sessionID)) { $headers['Cookie']=$sessionID; } // post submitted form xml to OpenQuotes portal server markup service $soapReturn = postMessage($markupServiceUrl, $xmlDocument, $headers); $soapReturnXML = $soapReturn['xml']; } /** * Post an xml message to a specified HTTP connection and read the response (ie contact a portal sevice) * @param urlString url to connect to * @param connection Portal service HTTP connection * @param xmlToSend xml message document to post to connection * @return response header and xml in array */ function postMessage($markupServiceUrl, $xmlToSend, $headers) { if(!isset($headers)) { $headers = array(); } // split the url into its component parts (host, port etc) $urlParts = parse_url($markupServiceUrl); // convert the xml document to a string and write it to the HTTP connection. $xmlString = $xmlToSend->asXML(); // empty string ready to write response to $response = ''; // open connection with service $fp = fsockopen($urlParts['host'], $urlParts['port']); if ($fp) { // create header to post $out = "POST ".$markupServiceUrl." HTTP/1.1\r\n"; $out .= "Host: ".$urlParts[host]."\r\n"; $out .= "Accept: text/xml\r\n"; $out .= "Connection: Close\r\n"; $out .= "Content-Type: text/xml\r\n"; foreach($headers as $headerName => $headerValue) { // if the header value is not null, add it to the request if (!is_null($headerValue)) { $out .= $headerName.": ".$headerValue."\r\n"; } } $out .= "Content-Length: " . strlen($xmlString) . "\r\n"; $out .= "\r\n"; // add xml for posting $out .= $xmlString; // post header and xml to service fwrite($fp, $out); // read service response while (!feof($fp)) { $response .= fgets($fp, 128); } // close connection fclose($fp); } else { echo "ERROR: no connection to ".$markupServiceUrl." <br>\n"; } // read the response from the connection $responseHeaders = array(); // for each line in the response get the headers, then finish when the xml is located $lines = explode("\n",$response); for ($i=0; $i<count($lines); $i++) { $line = $lines[$i]; // split line into two parts to extract header parameter name and values $parts = explode(": ",$line, 2); if(count($parts) == 2) { // 2 parts (line contains a colon) means it is a header line $responseHeaders[$parts[0]] = chop($parts[1]); } if($i>0 && (count($parts) == 1)) { // just one part means the line is the spacer line between the header lines and the body // so extract the xml (body) from the end of the response and exit the for loop $lines = explode("\n",$response,$i+2); $resultXML = $lines[$i+1]; $i=$i+2; } } // xml and headers to return $soapReturn['xml']=simplexml_load_string($resultXML); $soapReturn['header']=$responseHeaders; // check result xml for a soap fault (error) checkForSoapFault($soapReturn['xml']); // return the response xml document return $soapReturn; } /** * report error details of a soap fault response * @param SimpleXML $soapResponseXML **/ function checkForSoapFault($soapResponseXML) { // check for soap fault $soapResponseXML->registerXPathNamespace("env", "http://schemas.xmlsoap.org/soap/envelope/"); $faultElements = $soapResponseXML->xpath('//env:Fault'); if ($faultElements!=FALSE) { // if soap fault found // extract the faultcode, faultstring and faultactor from the service response (XML) $faultCodeElements = $faultElements[0]->xpath('//faultcode/text()'); if ($faultCodeElements==FALSE || is_null($faultCodeElements[0]) || empty($faultCodeElements[0])) { $faultCode=""; } else { $faultActor=(string) $faultCodeElements[0]; } $faultStringElements = $faultElements[0]->xpath('//faultstring/text()'); if ($faultStringElements==FALSE || is_null($faultStringElements[0]) || empty($faultStringElements[0])) { $faultString=""; } else { $faultString=(string) $faultStringElements[0]; } $faultActorElements = $faultElements[0]->xpath('//faultactor/text()'); if ($faultActorElements==FALSE || is_null($faultActorElements[0]) || empty($faultActorElements[0])) { $faultActor=""; } else { $faultActor=(string) $faultActorElements[0]; } // output error details to page echo "<br>\n"; echo "SOAP ERROR REPORTED<br>\n"; echo "faultCode: ".$faultCode."<br>\n"; echo "faultString: ".$faultString."<br>\n"; echo "faultActor: ".$faultActor."<br>\n"; echo "<br>\n"; } } /** * Save the registration handle for use outside of a single user session * @param String $registrationHandle **/ function persistRegistrationHandle($registrationHandle) { global $registrationHandleFile; // set an application variable to hold the registration handle for later reuse // create registrationhandle.txt if it does not already exist $file=fopen($registrationHandleFile,'w'); fwrite($file,$registrationHandle); fclose($file); } /** * Get the persisted registration handle * @return String registration handle **/ function getPersistedRegistrationHandle() { global $registrationHandleFile; return file_get_contents($registrationHandleFile); } // process page session_start(); // initialise session and application variables // get the current registrationHandle being used for connection to the OpenQuote portal server (for display on the page) $registrationHandle = getPersistedRegistrationHandle(); // get OpenQuote product being currenly accessed if(!isset($_SESSION['OpenQuoteProduct']) || is_null($_SESSION['OpenQuoteProduct'])) { $_SESSION['OpenQuoteProduct'] = ""; } $openQuoteProduct=$_SESSION['OpenQuoteProduct']; // get session id being used for current product if(!isset($_SESSION[$openQuoteProduct . ".ProductSessionID"]) || is_null($_SESSION[$openQuoteProduct . ".ProductSessionID"])) { $_SESSION[$openQuoteProduct . ".ProductSessionID"] = ""; } $productSessionId=$_SESSION[$openQuoteProduct . ".ProductSessionID"]; // initialise variable to hold OpenQuote portal HTML for inclusion on web page $openQuotePageHTML = ""; // check to see if product has been changed by user (and changeProduct submit button pressed) if (isset($_REQUEST["changeProduct"]) && !is_null($_REQUEST["changeProduct"]) && $_REQUEST["openquote_product"]!=$openQuoteProduct) { // if so set new product as the product to be quoted for $openQuoteProduct = $_REQUEST["openquote_product"]; $_SESSION['OpenQuoteProduct'] = $openQuoteProduct; // different products require different session ids, // so look to see if a sessionID has previosly been used for the newly selected product // if one hasn't, then it will be cleared and setup during the next Markup Requester service call $productSessionId=$_SESSION[$openQuoteProduct . '.ProductSessionID']; } // check to see if restart quote process has been selected by user if (isset($_REQUEST["resetProduct"]) && !is_null($_REQUEST["resetProduct"])) { // to restart a quote process, simply clear the session id for the product // so the next Markup Request service call is forced to generate a new one $_SESSION[$openQuoteProduct . ".ProductSessionID"] = ""; $productSessionId = ""; } // deregister consumer test button // normally the deregister process would be completed autmatically by code when the // web application is stopped, but for simplicity a manual button press controls the process here. if (isset($_REQUEST["deregister"]) && !is_null($_REQUEST["deregister"])) { // deregister the consumer deregisterConsumer(); // finally, for neatness, clear the old registration handle variable and product $registrationHandle = ""; $_SESSION['OpenQuoteProduct'] = ""; $openQuoteProduct = ""; } // if the submit was none of the above (ie not new product or restart quote process, or deregistration) // then assume it was a portal submit and pass the form parameters to OpenQuote if(!isset($_REQUEST["changeProduct"]) && !isset($_REQUEST["resetProduct"]) && !isset($_REQUEST["deregister"])) { // open submit if a product is selected if (!is_null($openQuoteProduct) && !empty($openQuoteProduct)) { // submit the openquote portlet form to the portal markup request service submitWSRPForm(); } } // Request the next page from the OpenQuote portal if a product has been previously specifed if(!empty($openQuoteProduct)) { // get the openquote product form HTML from the portal markup request service $openQuotePageHTML = requestWSRPForm(); // if this is the first time (or a restart) of the quote process, // then get the new session id returned fron the portal markup request service (for display on page) if (is_null($productSessionId) || empty($productSessionId)) { $productSessionId = $_SESSION[$openQuoteProduct . '.ProductSessionID']; } // if this is the first time the cosumer has accessed the portal // then get the new registrationHandle (for display on page) if (is_null($registrationHandle) || empty($registrationHandle)) { $registrationHandle = getRegistrationHandle(); } } ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <script type="text/javascript"></script> <title>OpenQuote Portlet Consumer Example using PHP</title> </head> <body> <h1>OpenQuote Portlet Consumer Example using PHP</h1> <p> This PHP page is using the generic portlet provided by OpenQuote. </p> <p> The consumer registration handle is <?php echo $registrationHandle; ?>. </p> <p> The users session id is <?php echo $productSessionId; ?> </p> <form NAME='setup' METHOD="POST" ACTION="index.php"> <?php if(!is_null($registrationHandle) && !empty($registrationHandle)) { ?> <br/> <input TYPE="submit" name="deregister" value="Deregister Consumer <?php echo $registrationHandle; ?>" /> Normally this sould be done by the web application as part of its clean up code when it is stopped, however for the purposes of this simple demonstration it is a manual process <br/> <?php } ?> <br/> Please enter product to quote for (i.e. AIL.Demo.MotorPlus): <br/> Product: <input name="openquote_product" value="<?php echo $openQuoteProduct; ?>" /> <input TYPE="submit" name="changeProduct" value="Change product" /> <?php if(!is_null($openQuoteProduct) && !empty($openQuoteProduct)) { ?> <input TYPE="submit" name="resetProduct" value="Restart quote process for <?php echo $openQuoteProduct; ?>" /> <?php } ?> </form> <?php if(!is_null($openQuoteProduct) && !empty($openQuoteProduct)) { ?> <h1>Quote for <?php echo $openQuoteProduct; ?></h1> <?php } ?> <?php echo $openQuotePageHTML; ?> </body> </html>
[edit] RegisterWSRPConsumer.xml XML Document
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header /> <env:Body> <ns1:register xmlns:ns1="urn:oasis:names:tc:wsrp:v1:types" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ns1:consumerName></ns1:consumerName> <ns1:consumerAgent>TestPHP.1.0</ns1:consumerAgent> <ns1:methodGetSupported>false</ns1:methodGetSupported> </ns1:register> </env:Body> </env:Envelope>
[edit] DeregisterWSRPConsumer.xml XML Document
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:oasis:names:tc:wsrp:v1:types"> <env:Header/> <env:Body> <ns1:deregister> <ns1:registrationHandle></ns1:registrationHandle> </ns1:deregister> </env:Body> </env:Envelope>
[edit] RequestWSRPForm.xml XML Document
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header /> <env:Body> <ns1:getMarkup xmlns:ns1="urn:oasis:names:tc:wsrp:v1:types" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ns1:registrationContext> <ns1:registrationHandle></ns1:registrationHandle> </ns1:registrationContext> <ns1:portletContext> <ns1:portletHandle>quotation.WSRPQuotationPortlet</ns1:portletHandle> </ns1:portletContext> <ns1:runtimeContext> <ns1:userAuthentication>wsrp:none</ns1:userAuthentication> <ns1:portletInstanceKey>portletinstance</ns1:portletInstanceKey> <ns1:namespacePrefix>portlet_space</ns1:namespacePrefix> </ns1:runtimeContext> <ns1:userContext xsi:nil="1" /> <ns1:markupParams> <ns1:secureClientCommunication>false</ns1:secureClientCommunication> <ns1:locales>en-GB</ns1:locales> <ns1:mimeTypes>text/html</ns1:mimeTypes> <ns1:mode>wsrp:view</ns1:mode> <ns1:windowState>wsrp:normal</ns1:windowState> <ns1:clientData> <ns1:userAgent>Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-GB;rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3</ns1:userAgent> </ns1:clientData> </ns1:markupParams> </ns1:getMarkup> </env:Body> </env:Envelope>
[edit] SubmitWSRPForm.xml XML Document
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header/> <env:Body> <ns1:performBlockingInteraction xmlns:ns1="urn:oasis:names:tc:wsrp:v1:types" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ns1:registrationContext> <ns1:registrationHandle></ns1:registrationHandle> </ns1:registrationContext> <ns1:portletContext> <ns1:portletHandle>quotation.WSRPQuotationPortlet</ns1:portletHandle> </ns1:portletContext> <ns1:runtimeContext> <ns1:userAuthentication>wsrp:none</ns1:userAuthentication> <ns1:portletInstanceKey>portletinstance</ns1:portletInstanceKey> <ns1:namespacePrefix>portlet_space</ns1:namespacePrefix> </ns1:runtimeContext> <ns1:userContext xsi:nil="1"/> <ns1:markupParams> <ns1:secureClientCommunication>false</ns1:secureClientCommunication> <ns1:locales>en-GB</ns1:locales> <ns1:mimeTypes>text/html</ns1:mimeTypes> <ns1:mode>wsrp:view</ns1:mode> <ns1:windowState>wsrp:normal</ns1:windowState> <ns1:clientData> <ns1:userAgent>Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-GB; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3</ns1:userAgent> </ns1:clientData> </ns1:markupParams> <ns1:interactionParams> <ns1:portletStateChange>cloneBeforeWrite</ns1:portletStateChange> </ns1:interactionParams> </ns1:performBlockingInteraction> </env:Body> </env:Envelope>

