/** * Method to transform a type to publish it in the WSDL file * * @param array $field Field definition. * @param SimpleXMLElement &$sequence XML with the fields sequence * @param SimpleXMLElement &$typeSchema XML of the typeSchema in case new derived types need to be added * @param string $elementName Parent element name to add the new derived types with unique names * @param boolean $validateOptional Optional parameter to validate if the field is optional. Otherwise it's always set as required * @param array $extraFields Array of extra fields to process - in case of array types * @param SimpleXMLElmenet $complexArrays Complex arrays definitions * * @return void */ public function wsdlField($field, &$sequence, &$typeSchema, $elementName, $validateOptional = false, $extraFields = array(), $complexArrays = null) { parent::wsdlField($field, $sequence, $typeSchema, $elementName, $validateOptional); $this->element->addAttribute('type', 'tns:' . $elementName . '_' . $field['name']); if (!empty($extraFields)) { RApiSoapHelper::addElementFields($extraFields, $typeSchema, $elementName . '_' . $field['name'], false, '', $complexArrays); } }
/** * Method to transform a type to publish it in the WSDL file * * @param array $field Field definition. * @param SimpleXMLElement &$sequence XML with the fields sequence * @param SimpleXMLElement &$typeSchema XML of the typeSchema in case new derived types need to be added * @param string $elementName Parent element name to add the new derived types with unique names * @param boolean $validateOptional Optional parameter to validate if the field is optional. Otherwise it's always set as required * @param array $extraFields Array of extra fields to process - in case of array types * @param SimpleXMLElmenet $complexArrays Complex arrays definitions * * @return void */ public function wsdlField($field, &$sequence, &$typeSchema, $elementName, $validateOptional = false, $extraFields = array(), $complexArrays = null) { if (!isset($this->element)) { $this->element = $sequence->addChild('element', null, 'http://www.w3.org/2001/XMLSchema'); } if (!isset($this->element['minOccurs'])) { $this->element->addAttribute('minOccurs', $validateOptional && RApiHalHelper::isAttributeTrue($field, 'isRequiredField') || !$validateOptional ? '1' : '0'); } $this->element->addAttribute('maxOccurs', 'unbounded'); if (!isset($this->element['name']) && isset($field['name'])) { $this->element->addAttribute('name', $field['name']); } $this->element->addAttribute('type', 'tns:' . $elementName . '_' . $field['name']); if (!empty($extraFields)) { RApiSoapHelper::addElementFields($extraFields, $typeSchema, $elementName . '_' . $field['name'], true, '', $complexArrays); } }
/** * Method to send the application response to the client. All headers will be sent prior to the main * application output data. * * @return void * * @since 1.4 */ public function render() { $documentOptions = array('absoluteHrefs' => $this->options->get('absoluteHrefs', false), 'documentFormat' => 'xml'); if ($this->operation == 'wsdl') { // Needed for formatting $dom = dom_import_simplexml($this->wsdl)->ownerDocument; $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $body = $dom->saveXML(); } else { // Add error faults if they exist if ($this->webservice->statusCode >= 300) { // We must have status of 200 for SOAP communication even if it is fault $this->setStatusCode(200); $faultResponse = $this->prepareFaultResponseMessage($this->webservice); $body = RApiSoapHelper::createSoapFaultResponse($faultResponse); } else { $body = $this->getBody(); } } JFactory::$document = new RApiSoapDocumentDocument($documentOptions, $this->operation == 'wsdl' ? 'xml' : 'soap+xml'); $body = $this->triggerFunction('prepareBody', $body); // Push results into the document. JFactory::$document->setApiObject($this)->setBuffer($body)->render(false); }
/** * Returns generated WSDL file for the webservice * * @param string $wsdlPath Path of WSDL file * * @return SimpleXMLElement */ public function generateWsdl($wsdlPath) { $wsdlFullPath = JUri::root() . $wsdlPath; $client = RApiHalHelper::attributeToString($this->webserviceXml, 'client', 'site'); $version = !empty($this->webserviceXml->config->version) ? $this->webserviceXml->config->version : '1.0.0'; $this->webserviceFullName = $client . '.' . $this->webserviceXml->config->name . '.' . $version; $this->webserviceUrl = RApiHalHelper::buildWebserviceFullUrl($client, $this->webserviceXml->config->name, $version, 'soap'); // Root of the document $this->wsdl = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?><wsdl:definitions' . ' xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"' . ' xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"' . ' xmlns:tns="' . $wsdlFullPath . '"' . ' xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap12/"' . ' xmlns:s="http://www.w3.org/2001/XMLSchema"' . ' xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"' . ' targetNamespace="' . $wsdlFullPath . '"' . ' xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"' . ' ></wsdl:definitions>', 0, false, 'wsdl', true); $types = $this->wsdl->addChild('types'); $this->typeSchema = $types->addChild('schema', null, 'http://www.w3.org/2001/XMLSchema'); $this->typeSchema->addAttribute('targetNamespace', $wsdlFullPath); $this->typeSchema->addAttribute('elementFormDefault', 'unqualified'); $this->addGlobalTypes($this->typeSchema); // Adding service $this->wsdlServices = $this->wsdl->addChild('service'); $this->wsdlServices->addAttribute('name', $this->webserviceFullName); $this->wsdlServices->addChild('documentation', $this->webserviceXml->description); // Add new port binding $port = $this->wsdlServices->addChild('port'); $port->addAttribute('name', $this->webserviceFullName . '_Soap'); $port->addAttribute('binding', 'tns:' . $this->webserviceFullName); // Add soap addresses $soapAddress = $port->addChild('soap:address', null, 'http://schemas.xmlsoap.org/wsdl/soap12/'); $soapAddress->addAttribute('location', $this->webserviceUrl); // Add webservice operations if (isset($this->webserviceXml->operations)) { // Read list if (isset($this->webserviceXml->operations->read->list)) { $filters = RApiHalHelper::getFilterFields($this->webserviceXml->operations->read->list, true, true); if (empty($filters)) { $filtersDef = array('name' => 'filters', 'transform' => 'array'); } else { $filtersDef = array('name' => 'filters', 'transform' => 'arraydefined', 'fields' => $filters); } // Add read list messages $inputFields = array(array('name' => 'limitStart', 'transform' => 'int'), array('name' => 'limit', 'transform' => 'int'), array('name' => 'filterSearch', 'transform' => 'string'), $filtersDef, array('name' => 'ordering', 'transform' => 'string'), array('name' => 'orderingDirection', 'transform' => 'string'), array('name' => 'language', 'transform' => 'string')); // Add read list response messages $outputFields = array(array('name' => 'list', 'transform' => 'arrayrequired', 'fields' => array(array('name' => 'item', 'maxOccurs' => 'unbounded', 'transform' => 'arrayrequired', 'fields' => RApiSoapHelper::getOutputResources($this->webserviceXml->operations->read->list, 'listItem'))))); $this->addOperation($this->wsdl, 'readList', $inputFields, $outputFields, true, true); } // Read item if (isset($this->webserviceXml->operations->read->item)) { // Add read item messages $inputFields = array_merge(RApiHalHelper::getFieldsArray($this->webserviceXml->operations->read->item, true), array(array('name' => 'language', 'transform' => 'string'))); // Add read item response messages $outputFields = array(array('name' => 'item', 'typeName' => 'item', 'transform' => 'arrayrequired', 'fields' => RApiSoapHelper::getOutputResources($this->webserviceXml->operations->read->item))); $this->addOperation($this->wsdl, 'readItem', $inputFields, $outputFields, false, true); } // Create operation if (isset($this->webserviceXml->operations->create)) { // Add create messages $inputFields = RApiHalHelper::getFieldsArray($this->webserviceXml->operations->create); // Add create response messages $outputFields = array(RApiSoapHelper::getResultResource($this->webserviceXml->operations->create)); $this->addOperation($this->wsdl, 'create', $inputFields, $outputFields, true); } // Update operation if (isset($this->webserviceXml->operations->update)) { // Add update messages $inputFields = RApiHalHelper::getFieldsArray($this->webserviceXml->operations->update); // Add update response messages $outputFields = array(RApiSoapHelper::getResultResource($this->webserviceXml->operations->update)); $this->addOperation($this->wsdl, 'update', $inputFields, $outputFields, true); } // Delete operation if (isset($this->webserviceXml->operations->delete)) { // Add delete messages $inputFields = RApiHalHelper::getFieldsArray($this->webserviceXml->operations->delete, true); // Add delete response messages $outputFields = array(RApiSoapHelper::getResultResource($this->webserviceXml->operations->delete)); $this->addOperation($this->wsdl, 'delete', $inputFields, $outputFields); } // Task operation if (isset($this->webserviceXml->operations->task)) { foreach ($this->webserviceXml->operations->task->children() as $taskName => $task) { // Add task messages $inputFields = RApiHalHelper::getFieldsArray($task); // Add task response messages $outputFields = array(RApiSoapHelper::getResultResource($task)); $this->addOperation($this->wsdl, 'task_' . $taskName, $inputFields, $outputFields, true); } } } return $this->wsdl; }
/** * Method to save the form data to XML file. * * @param array $data The form data. * * @return boolean True on success, False on error. * * @throws RuntimeException * * @since 1.4 */ public function saveXml($data) { $dataRegistry = new JRegistry($data); $item = null; if (empty($data['main']['name'])) { $this->setError(JText::_('COM_REDCORE_WEBSERVICE_NAME_FIELD_CANNOT_BE_EMPTY')); return false; } if (!empty($data['main']['id'])) { $item = $this->getItem($data['main']['id']); } $client = $dataRegistry->get('main.client', 'site'); $name = $dataRegistry->get('main.name', ''); $version = $dataRegistry->get('main.version', '1.0.0'); $folder = $dataRegistry->get('main.path', ''); $folder = !empty($folder) ? JPath::clean('/' . $folder) : ''; if (!JFolder::exists(RApiHalHelper::getWebservicesPath() . $folder)) { JFolder::create(RApiHalHelper::getWebservicesPath() . $folder); } $fullPath = JPath::clean(RApiHalHelper::getWebservicesPath() . $folder . '/' . $client . '.' . $name . '.' . $version . '.xml'); $xml = new SimpleXMLElement('<?xml version="1.0"?><apiservice client="' . $client . '"></apiservice>'); $xml->addChild('name', $dataRegistry->get('main.title', $name)); $xml->addChild('author', $dataRegistry->get('main.author', '')); $xml->addChild('copyright', $dataRegistry->get('main.copyright', '')); $xml->addChild('description', $dataRegistry->get('main.description', '')); $configXml = $xml->addChild('config'); $configXml->addChild('name', $dataRegistry->get('main.name', '')); $configXml->addChild('version', $version); $configXml->addChild('authorizationAssetName', $dataRegistry->get('main.authorizationAssetName', '')); $operationsXml = $xml->addChild('operations'); $readXml = null; $taskXml = null; foreach ($data as $operationName => $operation) { if ($operationName != 'main') { if (empty($operation['isEnabled'])) { continue; } $operationNameSplit = explode('-', $operationName); if ($operationNameSplit[0] == 'read' && count($operationNameSplit) > 1) { if (is_null($readXml)) { $readXml = $operationsXml->addChild('read'); } $operationXml = $readXml->addChild($operationNameSplit[1]); } elseif ($operationNameSplit[0] == 'task' && count($operationNameSplit) > 1) { if (is_null($taskXml)) { $taskXml = $operationsXml->addChild('task'); } $operationXml = $taskXml->addChild($operationNameSplit[1]); } else { $operationXml = $operationsXml->addChild($operationNameSplit[0]); } $this->getOperationAttributesFromPost($operationXml, $data, $operationName); $this->getFieldsFromPost($operationXml, $data, $operationName); $this->getResourcesFromPost($operationXml, $data, $operationName); } } // Needed for formatting $dom = dom_import_simplexml($xml)->ownerDocument; $dom->preserveWhiteSpace = false; $dom->formatOutput = true; if ($dom->save($fullPath)) { if (!empty($item->id)) { $folder = !empty($item->path) ? '/' . $item->path : ''; $oldPath = JPath::clean(RApiHalHelper::getWebservicesPath() . $folder . '/' . $item->xmlFile); if ($oldPath != $fullPath) { if (JFile::exists($oldPath)) { JFile::delete($oldPath); } } } $wsdl = RApiSoapHelper::generateWsdl($xml); $fullWsdlPath = substr($fullPath, 0, -4) . '.wsdl'; return RApiSoapHelper::saveWsdlContentToPath($wsdl, $fullWsdlPath); } return false; }
/** * Read item * * @param object $data Primary keys and $language * * @return array */ public function readItem($data) { // We are setting the operation of the webservice to Read $this->setOperation('read'); $dataGet = $this->webservice->options->get('dataGet', array()); $primaryKeysFromFields = RApiHalHelper::getFieldsArray($this->webservice->configuration->operations->read->item, true); // If there are no primary keys defined we will use id field as default if (empty($primaryKeysFromFields)) { $primaryKeysFromFields['id'] = array('transform' => 'int'); } foreach ($primaryKeysFromFields as $primaryKey => $primaryKeyField) { $keyData = ''; if (isset($data->{$primaryKey}) && $data->{$primaryKey} != '') { $keyData = $data->{$primaryKey}; } $dataGet->{$primaryKey} = $this->webservice->transformField($primaryKeyField['transform'], $keyData, false); } // Handle different language switch $this->setLanguage((string) (isset($data->language) ? $data->language : '')); $this->webservice->options->set('dataGet', $dataGet); $this->webservice->options->set('task', ''); $this->webservice->options->set('filterOutResourcesGroups', array('_links', '_messages')); $this->webservice->execute(); $arr = $this->webservice->hal->toArray(); $outputResources = RApiSoapHelper::getOutputResources($this->webservice->configuration->operations->read->item, '', true); $response = RApiSoapHelper::selectListResources($outputResources, array($arr)); $final = new stdClass(); $final->item = empty($response) ? array() : $response[0]; $match = true; if (RApiHalHelper::isAttributeTrue($this->webservice->configuration->operations->read->item, 'enforcePKs', true)) { foreach ($primaryKeysFromFields as $primaryKey => $primaryKeyField) { if ($dataGet->{$primaryKey} != $final->item->{$primaryKey}) { $match = false; } } } if (!$match) { $final = array(); } if (!count((array) $final->item)) { $final = array(); } return $final; }
/** * Method to register custom library. * * @return void */ public function onAfterInitialise() { if (defined('REDCORE_LIBRARY_LOADED')) { $apiName = JFactory::getApplication()->input->getString('api'); if ($this->isApiEnabled($apiName)) { $input = JFactory::getApplication()->input; if (!empty($apiName)) { try { // We will disable all error messaging from PHP from the output error_reporting(0); ini_set('display_errors', 0); JError::setErrorHandling(E_ERROR, 'message'); JFactory::getApplication()->clearHeaders(); $webserviceClient = $input->get->getString('webserviceClient', ''); $optionName = $input->get->getString('option', ''); $optionName = strpos($optionName, 'com_') === 0 ? substr($optionName, 4) : $optionName; $viewName = $input->getString('view', ''); $version = $input->getString('webserviceVersion', ''); $token = $input->getString(RBootstrap::getConfig('oauth2_token_param_name', 'access_token'), ''); $apiName = ucfirst($apiName); $method = strtoupper($input->getMethod()); $task = RApiHalHelper::getTask(); $data = RApi::getPostedData(); $dataGet = $input->get->getArray(); if (empty($webserviceClient)) { $webserviceClient = JFactory::getApplication()->isAdmin() ? 'administrator' : 'site'; } $options = array('api' => $apiName, 'optionName' => $optionName, 'viewName' => $viewName, 'webserviceVersion' => $version, 'webserviceClient' => $webserviceClient, 'method' => $method, 'task' => $task, 'data' => $data, 'dataGet' => $dataGet, 'accessToken' => $token, 'format' => $input->getString('format', $this->params->get('webservices_default_format', 'json')), 'id' => $input->getString('id', ''), 'absoluteHrefs' => $input->get->getBool('absoluteHrefs', true)); // Create instance of Api and fill all required options $api = RApi::getInstance($options); // Run the api task $api->execute(); // Display output $api->render(); } catch (Exception $e) { $code = $e->getCode() > 0 ? $e->getCode() : 500; if (strtolower($apiName) == 'soap') { // We must have status of 200 for SOAP communication even if it is fault $message = RApiSoapHelper::createSoapFaultResponse($e->getMessage()); header("Content-Type: soap+xml"); header("Content-length: " . strlen($message)); header("Status: 200"); echo $message; } else { // Set the server response code. header('Status: ' . $code, true, $code); // Check for defined constants if (!defined('JSON_UNESCAPED_SLASHES')) { define('JSON_UNESCAPED_SLASHES', 64); } // An exception has been caught, echo the message and exit. echo json_encode(array('message' => $e->getMessage(), 'code' => $e->getCode(), 'type' => get_class($e)), JSON_UNESCAPED_SLASHES); } } JFactory::getApplication()->close(); } } } }
/** * Method to parse through a webservices element of the installation manifest and take appropriate * action. * * @param JInstallerAdapter $parent class calling this method * * @return boolean True on success * * @since 1.3 */ public function installWebservices($parent) { $installer = $this->getInstaller(); $manifest = $this->getManifest($parent); $src = $parent->getParent()->getPath('source'); if (!$manifest) { return false; } $installer->setPath('source', $src); $element = $manifest->webservices; if (!$element || !count($element->children())) { // Either the tag does not exist or has no children therefore we return zero files processed. return false; } // Here we set the folder we are going to copy the files from. $folder = (string) $element->attributes()->folder; // This prevents trying to install webservice from other extension directory if webservice folder is not set if (!$folder || !is_dir($src . '/' . $folder)) { return false; } // Here we set the folder we are going to copy the files to. $destination = JPath::clean(RApiHalHelper::getWebservicesPath()); $source = $src . '/' . $folder; $copyFiles = $this->prepareFilesForCopy($element, $source, $destination); // Copy the webservice XML files $return = $installer->copyFiles($copyFiles, true); // Recreate or create new SOAP WSDL files if (method_exists('RApiSoapHelper', 'generateWsdlFromFolder')) { foreach ($element->children() as $file) { RApiSoapHelper::generateWsdlFromFolder($destination . '/' . $file); } } return $return; }