![]() |
|
ckWebServicePlugin - 3.0.0WebService API Plugin. |
|
![]() |
63
users
Sign-in
to change your status |
The ckWebServicePlugin allows you to build a webservice api for your symfony applications. It comes with a powerful and easy to use wsdl generator, which creates WS-I compliant wsdl files for a maximum of interopability with PHP, .NET and Java clients. |
| Name | Status | |
|---|---|---|
|
|
lead | ed.bew <<ta>> lrek-naitsirhc |
Copyright (c) 2008 Christian Kerl
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
| Version | License | API | Released |
|---|---|---|---|
| 4.0.0stable | MIT license | 4.0.0stable | 18/06/2010 |
| Version | License | API | Released |
|---|---|---|---|
| 4.0.0stable | MIT license | 4.0.0stable | 18/06/2010 |
| Version | License | API | Released |
|---|---|---|---|
| 3.1.0stable | MIT license | 3.1.0stable | 09/01/2010 |
| 3.0.0stable | MIT license | 3.0.0stable | 17/01/2009 |
| Version | License | API | Released |
|---|---|---|---|
| 2.3.0stable | MIT license | 2.3.0stable | 08/08/2009 |
| 2.2.2stable | MIT license | 2.2.2stable | 16/01/2009 |
| 2.2.1stable | MIT license | 2.2.1stable | 20/11/2008 |
| 2.2.0stable | MIT license | 2.2.0stable | 20/11/2008 |
| 2.1.0stable | MIT license | 2.1.0stable | 19/08/2008 |
| 2.0.0devel | MIT license | 2.0.0devel | 19/07/2008 |
| Version | License | API | Released |
|---|---|---|---|
| 1.5.0stable | MIT license | 1.5.0stable | 19/08/2008 |
| 1.4.1stable | MIT license | 1.4.1stable | 19/07/2008 |
| 1.4.0stable | MIT license | 1.4.0stable | 17/06/2008 |
| 1.2.0stable | MIT license | 1.2.0stable | 10/04/2008 |
| 1.1.3stable | MIT license | 1.1.3stable | 29/03/2008 |
| 1.0.0stable | MIT license | 1.0.0stable | 12/03/2008 |
Array
Array
Array
The ckWebServicePlugin allows you to build a webservice api for your symfony applications. It comes with a powerful and easy to use wsdl generator, which creates WS-I compliant wsdl files for a maximum of interopability with PHP, .NET and Java clients.
To install the latest release, execute:
> symfony plugin:install ckWebServicePlugin
or to install the current revision, checkout the HEAD revision into a plugins/ckWebServicePlugin folder:
> svn co http://svn.symfony-project.com/plugins/ckWebServicePlugin/branches/1.2/
Now configure the plugin how it is described in the next section and clear your cache afterwards.
The configuration can be devided into two parts. A basic one, which is mandatory and has to be done in order to get the plugin working.
The second, advanced, part is only required under certain circumstances and when you want to leverage the full power of the plugin.
So if you are using this plugin the first time you can skip the Advanced section.
Configure general plugin settings in your application's app.yml file.
all:
# because by default every filter condition is true, we have to set this var
# to off in all other environments
enable_soap_parameter: off
# your environment for webservice mode
soap:
# enable the `ckSoapParameterFilter`
enable_soap_parameter: on
ck_web_service_plugin:
# the location of your wsdl file
wsdl: %SF_WEB_DIR%/myWebService.wsdl
# the class that will be registered as handler for webservice requests
handler: ckSoapHandler
You will propably have to change the wsdl and handler option after you have run the webservice:generate-wsdl task.
Enable the ckWebServiceController in your application's factories.yml file.
# your environment for webservice mode
soap:
controller:
class: ckWebServiceController
Enable the ckSoapParameterFilter in your application's filters.yml file.
soap_parameter:
class: ckSoapParameterFilter
param:
# `app_enable_soap_parameter` has to be set to `on` so the filter is only enabled in soap mode
condition: %APP_ENABLE_SOAP_PARAMETER%
In your application's app.yml file you have some more options to configure the internally used SoapServer.
These are:
setting the persistence mode:
# your environment for webservice mode soap: # ... ck_web_service_plugin: # ... persist: <?php echoln(SOAP_PERSISTENCE_SESSION) ?>
For further information see documentation on SoapServer::setPersistence().
setting the $options array used by SoapServer::__construct():
# your environment for webservice mode soap: # ... ck_web_service_plugin: # ... soap_options: encoding: utf-8 soap_version: <?php echoln(SOAP_1_2) ?>
For further information see documentation on SoapServer::__construct().
configuring SOAP Headers:
# your environment for webservice mode
soap:
# ...
ck_web_service_plugin:
# ...
soap_headers:
# the name of the soap header
MySoapHeader:
# the corresponding data class
class: MySoapHeaderDataClass
For more details about the usage of SOAP Headers read the section Using SOAP Headers
Every action, which should be callable in webservice mode, needs some configuration so the parameters are accessable through sfRequest::getParameter() and the proper value is returned as result.
This configuration is automaticly done by the webservice:generate-wsdl task, if you don't use the task or want to customize something you have to change the module.yml file corresponding to the action.
An example module.yml file:
# your environment for webservice mode
soap:
# the action name
action_name:
# ordered list of the parameters
parameter: [first_param, second_param]
# the result adapter
result:
# the result adapter class, extending `ckAbstractResultAdapter`
class: ckPropertyResultAdapter
# result adapter specific parameters array
param:
property: result
The result adapters will be explained in more detail in the section Understanding result adapters.
webservice:generate-wsdl taskNow it is time to start making our actions avialable as a webservice.
This is best explained with an example, we will use the following action, which will multiply two numbers and is in an application named frontend:
<?php // apps/frontend/modules/math/actions/actions.class.php class mathActions extends sfActions { /** * An action multiplying two numbers. * * @param sfRequest $request A sfRequest instance */ public function executeMultiply($request) { $factorA = $request->getParameter('a'); $factorB = $request->getParameter('b'); if(is_numeric($factorA) && is_numeric($factorB)) { $this->result = $factorA * $factorB; return sfView::SUCCESS; } else { return sfView::ERROR; } } }
The only thing we will have to do is updating the doc comment:
<?php // apps/frontend/modules/math/actions/actions.class.php class mathActions extends sfActions { /** * An action multiplying two numbers. * * @ws-enable * * @param double $a Factor A * @param double $b Factor B * * @return double The result */ public function executeMultiply($request) { // nothing changed here... } }
Changes:
@ws-enable doc tag was added to mark the action as available in webservice mode,sfRequest::getParameter() a @param doc tag with type, name and description was added,@return doc tag with type and description was added,@param doc tag for $request was removed, because it is not a real parameter required by the action multiply.Now we are ready to execute the webservice:generate-wsdl task. It is explained in detail in the section Thewebservice:generate-wsdltask in detail.
Execute the task with:
> symfony webservice:generate-wsdl frontend MathApi http://localhost/
Change
http://localhost/to the url you are using for development!
The task will generate a MathApi.wsdl and MathApi.php in your project's web/ folder.
We have to change the wsdl option in the application's app.yml file to MathApi.wsdl:
// apps/frontend/config/app.yml
# your environment for webservice mode
soap:
# ...
ck_web_service_plugin:
# the location of your wsdl file, relative to your project's `web/` folder
wsdl: %SF_WEB_DIR%/MathApi.wsdl
and we have to clear the cache.
Now it is time to create a test script to ensure everything is working properly. Please refer to
the section Functional Testing to see how to setup the test environment.
The script will be named mathApiTest.php and placed under the project's test/functional/ folder. It should look the following way:
<?php // test/functional/mathApiTest.php $app = 'frontend'; $debug = true; include_once(dirname(__FILE__).'/../bootstrap/soaptest.php'); $c = new ckTestSoapClient(); // test executeMultiply $c->math_multiply(5, 2) // call the action ->isFaultEmpty() // check there are no errors ->isType('', 'double') // check the result type is double ->is('', 10); // check the result value is 10
You see the name of the webservice method follows the scheme <moduleName>_<actionName>, because this might be not descriptive enough or an alternative scheme is desired,
we will see how to change this. For this we have to change again the action's doc comment:
<?php // apps/frontend/modules/math/actions/actions.class.php class mathActions extends sfActions { /** * An action multiplying two numbers. * * @ws-enable * @ws-method SimpleMultiply * * @param double $a Factor A * @param double $b Factor B * * @return double The result */ public function executeMultiply($request) { // nothing changed here... } }
Changes:
@ws-method doc tag was added, specifying the new name.Now we have to regenerate the wsdl, execute:
> symfony webservice:generate-wsdl -h frontend MathApi http://localhost/
Theh(handler) option was added to the command, if it is not set the@ws-methoddoc tag is ignored.
The handler option leads to the generation of a MathApiHandler.class.php in the application's lib/ folder.
To enable this handler we have to edit the application's app.yml file and set the handler to MathApiHandler:
// apps/frontend/config/app.yml
soap:
# ...
ck_web_service_plugin:
# ...
# the class that will be registered as handler for webservice requests
handler: MathApiHandler
Our test script has to be updated:
<?php // test/functional/mathApiTest.php $app = 'frontend'; $debug = true; include_once(dirname(__FILE__).'/../bootstrap/soaptest.php'); $c = new ckTestSoapClient(); // test executeMultiply $c->SimpleMultiply(5, 2) ->isFaultEmpty() ->isType('', 'double') ->is('', 10);
You now have a basic overview how to use the plugin, the following sections will explain more advanced features.
webservice:generate-wsdl task in detailIts general syntax is:
> symfony webservice:generate-wsdl [--environment=soap] [--debug] [--handler] app_name webservice_name webservice_base_url
It will do the following things:
'app_name' for actions with the @ws-enable doc tag,web/ folder as 'webservice_name'.wsdl,web/ folder with name 'webservice_name'.php,handler option is set, it will add a 'webservice_name'Handler.class.php to the 'app_name''s lib/ folder.The arguments explained in detail:
app_name:
@ws-enable doc tagwebservice_name:
webservice_base_url:
The options explained in detail:
environment (short e):
soapdebug (short d):
falsehandler (short h):
ckSoapHandlerfalseUntil now it wasn't explained how the result of an action is got, we have just seen, that the result was assigned to the $this->result property and a sfView constant was returned, like sfView::SUCCESS.
Because an action should be reusable in web and webservice mode, we can't rely on the return value, because in web mode it always has to be a template name.
For this reason the result adpater pattern was introduced. This means to get the action result an adapter object of a subclass of ckAbstractResultAdapter is used.
Which one is used is determined by the configuration in the action's module.yml file how it is shown in the Configuration->Advanced->module.yml section.
The param array in the module.yml file is passed to the result adapter's constructor and contains adapter specific settings.
There are three built-in adapters:
ckPropertyResultAdapter:
property:
resultpropertyckMethodResultAdapter:
method:
ckRenderResultAdapter:
sf_format is set to soap so template file names have to end with .soap.php, e.g.: indexSuccess.soap.phpstringYou can easily implement your own adapters by extending the ckAbstractResultAdapter class and overriding the abstract ckAbstractResultAdapter::getResult() method.
In the previous examples only simple types have been used for parameters and result values, but you propably want to use objects, arrays of simple types or arrays of complex types. To illustrate these features we will stick to the example used earlier.
Let's say we want to multiply any number of factors, not only two:
<?php // apps/frontend/modules/math/actions/actions.class.php class mathActions extends sfActions { /** * An action multiplying any number of factors. * * @ws-enable * @ws-method SimpleMultiply * * @param double[] $factors An array of factors * * @return double The result */ public function executeMultiply($request) { $this->result = 1; foreach($request->getParameter('factors') as $factor) { $this->result *= $factor; } } }
Changes:
@param doc tags for factor $a and $b have been replaced with one @param doc tag for the factors array,As you can see the array type is indicated by the [], you can add the square brackets to any type to identify an array of the type should be used.
Because array types are complex data types, we have to add a mapping to the application's app.yml file:
// apps/frontend/config/app.yml
soap:
# ...
ck_web_service_plugin:
# ...
soap_options:
classmap:
# mapping of wsdl types to PHP types
DoubleArray: ckGenericArray
The generated array type names follow the scheme
<TypeName>Array.Use
ckGenericArrayas PHP mapping type, so you can use the transferred array object like a normal PHP array (iterate, index, ...).
The last thing to do is: regenerate the wsdl file with the webservice:generate-wsdl task, don't forget the h option, and clear the cache.
Our test script might look like this now:
<?php // test/functional/mathApiTest.php $app = 'frontend'; $debug = true; include_once(dirname(__FILE__).'/../bootstrap/soaptest.php'); $c = new ckTestSoapClient(); // test executeMultiply $c->SimpleMultiply(array(1, 2, 3, 4)) ->isFaultEmpty() ->isType('', 'double') ->is('', 24);
As example for the use of classes, we will implement the multiplication example for complex numbers.
Because complex numbers aren't nativly supported in PHP, we have to create our own ComplexNumber.class.php in the applications lib/ folder with the following content:
<?php // apps/frontend/lib/ComplexNumber.class.php class ComplexNumber { /** * @var double */ public $realPart; /** * @var double */ public $imaginaryPart; public function __construct($realPart, $imaginaryPart) { $this->realPart = $realPart; $this->imaginaryPart = $imaginaryPart; } public function __toString() { return sprintf('%.2f + %.2fi', $this->realPart, $this->imaginaryPart); } public function multiply($c) { $real = $this->realPart * $c->realPart - $this->imaginaryPart * $c->imaginaryPart; $imaginary = $this->realPart * $c->imaginaryPart - $this->imaginaryPart * $c->realPart; return new ComplexNumber($real, $imaginary); } }
It is important to add the @var doc tag with the type and an optional desciption to the properties of the class, so they will appear in the wsdl file.
Now let's modify the mathActions class by adding a new action, called ComplexMultiply:
<?php // apps/frontend/modules/math/actions/actions.class.php class mathActions extends sfActions { // nothing changed here... /** * An action multiplying any number of complex factors. * * @ws-enable * @ws-method ComplexMultiply * * @param ComplexNumber[] $input * * @return ComplexNumber */ public function executeComplexMultiply($request) { $this->result = new ComplexNumber(1, 0); foreach($request->getParameter('input') as $c) { $this->result = $this->result->multiply($c); } } }
Again we have to update the classmap in the application's app.yml file:
// apps/frontend/config/app.yml
soap:
# ...
ck_web_service_plugin:
# ...
soap_options:
classmap:
# mapping of wsdl types to PHP types
DoubleArray: ckGenericArray
ComplexNumber: ComplexNumber
ComplexNumberArray: ckGenericArray
Last regenerate the wsdl file once more and clear the cache.
Our updated test script will look something like this:
<?php // test/functional/mathApiTest.php $app = 'frontend'; $debug = true; include_once(dirname(__FILE__).'/../bootstrap/soaptest.php'); class ClientComplexNumber { public $realPart; public $imaginaryPart; public function __construct($realPart, $imaginaryPart) { $this->realPart = $realPart; $this->imaginaryPart = $imaginaryPart; } } $options = array( 'classmap' => array( 'ComplexNumber' => 'ClientComplexNumber', ), ); $c = new ckTestSoapClient($options); // test executeMultiply // ... // test executeComplexMultiply $cn = new ComplexNumber(1, 0); $c->ComplexMultiply(array(clone $cn, clone $cn)) ->isFaultEmpty() ->isType('', 'ClientComplexNumber') ->isType('realPart', 'double') ->is('realPart', 1) ->isType('imaginaryPart', 'double') ->is('imaginaryPart', 0);
As you see, we have added a lightweight definition of the ComplexNumber class called ClientComplexNumber, because it is likely that you don't have the same class definition at client and server, only the names and types of the properties will match.
In this section you have learned how to work with arrays and classes, the next section covers the usage of SOAP Headers.
SOAP Headers provide a way to send additional information, which are not directly or semantically related to the original method call. An good example for this are authentication information, so the use of a certain method can be restricted to a group of users.
To demonstrate the support for SOAP Headers, we will stick to the simple multiplication example used previously.
First we will modify the mathActions class the following way:
<?php // apps/frontend/modules/math/actions/actions.class.php class mathActions extends sfActions { /** * An action multiplying two numbers. * * @ws-enable * @ws-method SimpleMultiply * @ws-header AuthHeader: AuthData * * @param double $a Factor A * @param double $b Factor B * * @return double The result */ public function executeMultiply($request) { $factorA = $request->getParameter('a'); $factorB = $request->getParameter('b'); if($this->getUser()->isAuthenticated() && is_numeric($factorA) && is_numeric($factorB)) { $this->result = $factorA * $factorB; return sfView::SUCCESS; } else { return sfView::ERROR; } } }
Changes:
@ws-header doc tag was added, specifying the name (AuthHeader) of the SOAP Header and the data class (AuthData), which holds the data of the SOAP header,To get this example working we have to define the AuthData class, so let's create a AuthData.class.php file in the application's lib folder with the following content:
<?php // apps/frontend/lib/AuthData.class.php class AuthData { /** * @var string */ public $username; /** * @var string */ public $password; }
Afterwards we have to edit the application's app.yml file:
// apps/frontend/config/app.yml
soap:
# ...
ck_web_service_plugin:
# ...
soap_headers:
AuthHeader:
class: AuthData
When the application receives a SOAP Header a webservice.handle_header event is dispatched (it is a notifyUntil event), it has two attributes, the first is header holding the name of the header and the second is data containing an instance of the header's data class.
To do the authentication stuff in our example we will define an AuthHeaderListener class by creating an AuthHeaderListener.class.php in the application's lib/ folder with the following content:
<?php // apps/frontend/lib/AuthHeaderListener.class.php class AuthHeaderListener { const HEADER = 'AuthHeader'; public static function handleAuthHeader($event) { if($event['header'] == self::HEADER) { if($event['data']->username == 'test' && $event['data']->password == 'secret') { sfContext::getInstance()->getUser()->setAuthenticated(true); } return true; } else { return false; } } }
We have to register this event listener in the application's configuration class (assuming the application's name is frontend, this would be frontendConfiguration.class.php).
The modified configuration class would look something like this:
<?php // apps/frontend/config/frontendConfiguration.class.php class frontendConfiguration { public function configure() { $this->dispatcher->connect('webservice.handle_header', array('AuthHeaderListener', 'handleAuthHeader')); } }
The example is now ready to work, regenerate the wsdl file and clear the cache.
The last missing thing is the updated test script:
<?php // test/functional/mathApiTest.php $app = 'frontend'; $debug = true; include_once(dirname(__FILE__).'/../bootstrap/soaptest.php'); class ClientComplexNumber { // ... } class ClientAuthData { public $username; public $password; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } } $options = array( 'classmap' => array( 'ComplexNumber' => 'ClientComplexNumber', 'AuthHeader' => 'ClientAuthData', ), ); $c = new ckTestSoapClient($options); // test executeMultiply $authData = new ClientAuthData('test', 'secret'); $c->addRequestHeader('AuthHeaderElement', $authData) ->SimpleMultiply(5, 2) ->isFaultEmpty() ->isHeaderType('AuthHeaderElement', 'ClientAuthData') ->isHeader('AuthHeaderElement.username', 'test') ->isHeader('AuthHeaderElement.password', 'secret') ->isType('', 'double') ->is('', 10); // test executeComplexMultiply // ...
When adding or accessing a SOAP Header its name has to end with
Element.
This section demonstrated the use of SOAP Headers, so now you have seen nearly all features this plugin has to offer.
The symfony framework promotes the paradigm of test driven development, so it is just natural that this plugin offers you
possibilities to test your webservices. The following two sections show you how to setup a test environment and how to use
ckTestSoapClient for testing.
The setup of a test environment is similar to the configuration described in the section Configuration, only the environment name
changes from soap to soaptest, though you can use any other name you like.
The changes to the configuration files are:
app.yml:
Add the configuration of the soap for soaptest environment, e.g.:
# ...
soaptest:
enable_soap_parameter: on
ck_web_service_plugin:
wsdl: %SF_WEB_DIR%/myWebService.wsdl
handler: ckSoapHandler
factories.yml:
Add the following configuration:
# ...
soaptest:
storage:
class: sfSessionTestStorage
param:
session_path: %SF_TEST_CACHE_DIR%/sessions
controller:
class: ckWebServiceController
filters.yml:
Remains unchanged, because it is environment independent.
To finish the setup you have to create a bootstrap script for the soaptest environment in the project's test/bootstrap/ folder.
It will be named soaptest.php and will have the following content:
<?php require_once dirname(__FILE__).'/../../config/ProjectConfiguration.class.php'; $configuration = ProjectConfiguration::getApplicationConfiguration($app, 'soaptest', isset($debug) ? $debug : true); sfContext::createInstance($configuration); // remove all cache sfToolkit::clearDirectory(sfConfig::get('sf_app_cache_dir'));
This is the same as the default functional.php script except the environment parameter of ProjectConfiguration::getApplicationConfiguration() changed to soaptest.
You have to run the
webservice:generate-wsdltask always twice, once for thesoapenvironment and once for thesoaptestenvironment, do not forget to set the--environmentswitch to the proper value.
ckTestSoapClientThe ckTestSoapClient class lets you dispatch webservice requests to your symfony application without the need of a webserver.
Additionally it offers several evaluation methods for the result of each request.
A good starting point for every test script is the following template:
<?php $app = 'frontend'; $debug = true; include_once(dirname(__FILE__).'/../bootstrap/soaptest.php'); $options = array( 'classmap' => array( ), ); $c = new ckTestSoapClient($options);
Change the $app variable to the name of the application you want to test.
The
$optionsarray supplied to the constructor is the same as the one of PHP's SoapClient constructor.
Calling a SOAP Action is quite easy, just use it as it would be a method of the ckTestSoapClient object:
$c->myMethod($param1, $param2);
The call does not directly return the result, instead it returns the ckTestSoapClient object, this offers you a so called fluent interface
how it is often found in the symfony framework.
To get the actual result you have to call the getResult() method:
$result = $c->myMethod($param1, $param2) ->getResult();
For evaluating the result, the ckTestSoapClient class offers three methods is() checks the value, isType() checks the type and isCount() checks the element count,
useful when the result is an array.
The first argument is always a child element selector, so you can easily access and check properties or array elements, the second argument is a value to check against.
A child element selector can either be empty so the result itself is accessed or arbitrary count of property names or array indexes separated by a . (dot).
Some examples for selectors:
'' accesses the result,'name' accesses the name property of the result object,'1.name' accesses the name property of the second object in the result array,'cities.0.name' accesses the name property of the first object in the cities array, which is a property of the result object. Various examples for the use of the three methods is(), isType() and isCount() can be found in the test scripts given in this README.
You can also add SOAP Headers for the next request with the addRequestHeader() method, whichs first parameter is the header name and the second is the data object, e.g.:
$c->addRequestHeader('MyHeaderElement', new MyHeaderData('content')) ->myMethodWithHeader();
The headers are cleared after each request, so do not forget to add them again if you need them more then once.
Similar to the evaluation methods for the result, there are three methods to evaluate the response headers. These are isHeader(), isHeaderType() and isHeaderCount().
The parameter list is the same, but the child element selector has to contain at least the header name.
All primitive PHP types are supported:
string maps to xsd:stringinteger or int maps to xsd:intfloat or double maps to xsd:doubleboolean or bool maps to xsd:booleanIf you often regenerate your wsdl file during development, you propably want to disable caching of this file, so changes become usable immediatly.
You can do this by modifying your php.ini:
soap.wsdl_cache_enabled=0
If you want to check in an action if it is executed in webservice mode, you can use the isSoapRequest() method, e.g.:
<?php class FooActions extends sfActions { /** * Some description... * * @ws-enable */ public function executeBar($request) { if($this->isSoapRequest()) { // do this only in webservice mode... } // do this always... } }
If you have any questions concerning the use of the plugin, send me an email to: christian-kerl [at] web [dot] de
If you have feature suggestions, bug reports, patches, usage examples for the documentation or want to become an active contributor, send me an email to: christian-kerl [at] web [dot] de
Any help is welcome!

