You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

625 lines
18 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
use Symfony\Component\Yaml\Yaml;
/**
*
*/
interface OIntegrate_Converter
{
/**
* @param $content
* @return mixed
*/
public function convert($content);
}
/**
*
*/
interface OIntegrate_Engine
{
/**
* @param $data
* @param $templateFile
* @return mixed
*/
public function process($data, $templateFile);
}
/**
*
*/
class OIntegrate
{
private $schemaVersion = [];
private $acceptTemplates = [];
/**
* @param $name
* @param $engineOutput
* @return OIntegrate_Engine_JavaScript|OIntegrate_Engine_Smarty|OIntegrate_Engine_Index
*/
public static function getEngine($name, $engineOutput)
{
switch ($name) {
case 'javascript':
return new OIntegrate_Engine_JavaScript();
case 'smarty':
return new OIntegrate_Engine_Smarty($engineOutput == 'tikiwiki');
case 'index':
return new OIntegrate_Engine_Index();
}
}
/**
* @param $from
* @param $to
* @return OIntegrate_Converter_Direct|OIntegrate_Converter_EncodeHtml|OIntegrate_Converter_HtmlToTiki|OIntegrate_Converter_TikiToHtml|OIntegrate_Converter_Indexer
*/
public static function getConverter($from, $to)
{
switch ($from) {
case 'html':
if ($to == 'tikiwiki') {
return new OIntegrate_Converter_HtmlToTiki();
} elseif ($to == 'html') {
return new OIntegrate_Converter_Direct();
}
break;
case 'tikiwiki':
if ($to == 'html') {
return new OIntegrate_Converter_TikiToHtml();
} elseif ($to == 'tikiwiki') {
return new OIntegrate_Converter_EncodeHtml();
}
break;
case 'index':
case 'mindex':
if ($to == 'index') {
return new OIntegrate_Converter_Indexer();
} elseif ($to == 'html') {
return new OIntegrate_Converter_Indexer('html');
} elseif ($to == 'tikiwiki') {
return new OIntegrate_Converter_Indexer('tikiwiki');
}
break;
}
}
/**
* @param string $url
* @param string $postBody url or json encoded post parameters
* @param bool $clearCache
* @return OIntegrate_Response
*/
public function performRequest($url, $postBody = null, $clearCache = false) // {{{
{
$cachelib = TikiLib::lib('cache');
$tikilib = TikiLib::lib('tiki');
$cacheKey = $url . $postBody;
if ($cache = $cachelib->getSerialized($cacheKey)) {
if (time() < $cache['expires'] && ! $clearCache) {
return $cache['data'];
}
$cachelib->invalidate($cacheKey);
}
$client = $tikilib->get_http_client($url);
$method = null;
if (empty($postBody)) {
$method = 'GET';
$http_headers = [
'Accept' => 'application/json,text/x-yaml',
'OIntegrate-Version' => '1.0',
];
} else {
$method = 'POST';
if (json_decode($postBody)) { // autodetect if the content type should be json
$requestContentType = 'application/json';
} else {
$requestContentType = 'application/x-www-form-urlencoded';
}
$http_headers = [
'Accept' => 'application/json,text/x-yaml',
'OIntegrate-Version' => '1.0',
'Content-Type' => $requestContentType,
];
$client->setRawBody($postBody);
}
if (count($this->schemaVersion)) {
$http_headers['OIntegrate-SchemaVersion'] = implode(', ', $this->schemaVersion);
}
if (count($this->acceptTemplates)) {
$http_headers['OIntegrate-AcceptTemplate'] = implode(', ', $this->acceptTemplates);
}
// merge with existing headers
$headers = $client->getRequest()->getHeaders();
$http_headers = array_merge($headers->toArray(), $http_headers);
$client->setHeaders($http_headers);
$client->setMethod($method);
$httpResponse = $client->send();
$content = $httpResponse->getBody();
$requestContentType = $httpResponse->getHeaders()->get('Content-Type');
$cacheControl = $httpResponse->getHeaders()->get('Cache-Control');
$response = new OIntegrate_Response();
$response->contentType = $requestContentType;
$response->cacheControl = $cacheControl;
if ($requestContentType) {
$mediaType = $requestContentType->getMediaType();
} else {
$mediaType = '';
}
$response->data = $this->unserialize($mediaType, $content);
$filter = new DeclFilter();
$filter->addCatchAllFilter('xss');
$response->data = $filter->filter($response->data);
$response->version = $httpResponse->getHeaders()->get('OIntegrate-Version');
$response->schemaVersion = $httpResponse->getHeaders()->get('OIntegrate-SchemaVersion');
if (! $response->schemaVersion && isset($response->data->_version)) {
$response->schemaVersion = $response->data->_version;
}
$response->schemaDocumentation = $httpResponse->getHeaders()->get('OIntegrate-SchemaDocumentation');
global $prefs;
if (empty($cacheControl)) {
$maxage = 0;
$nocache = false;
} else {
// Respect cache duration and no-cache asked for
$maxage = $cacheControl->getDirective('max-age');
$nocache = $cacheControl->getDirective('no-cache');
}
if ($maxage) {
$expiry = time() + $maxage;
$cachelib->cacheItem(
$cacheKey,
serialize(['expires' => $expiry, 'data' => $response])
);
// Unless service specifies not to cache result, apply a default cache
} elseif (empty($nocache) && $prefs['webservice_consume_defaultcache'] > 0) {
$expiry = time() + $prefs['webservice_consume_defaultcache'];
$cachelib->cacheItem($cacheKey, serialize(['expires' => $expiry, 'data' => $response]));
}
return $response;
}
/**
* @param string $type
* @param string $data
* @return array|mixed|null
*/
public function unserialize($type, $data)
{
if (empty($data)) {
return null;
}
switch ($type) {
case 'application/json':
case 'text/javascript':
if ($out = json_decode($data, true)) {
return $out;
}
// Handle invalid JSON too...
$fixed = preg_replace('/(\w+):/', '"$1":', $data);
$out = json_decode($fixed, true);
return $out;
case 'text/x-yaml':
return Yaml::parse($data);
default:
// Attempt anything...
if ($out = $this->unserialize('application/json', $data)) {
return $out;
}
if ($out = $this->unserialize('text/x-yaml', $data)) {
return $out;
}
}
}
/**
* @param $version
*/
public function addSchemaVersion($version)
{
$this->schemaVersion[] = $version;
}
/**
* @param $engine
* @param $output
*/
public function addAcceptTemplate($engine, $output)
{
$this->acceptTemplate[] = "$engine/$output";
}
}
/**
*
*/
class OIntegrate_Response
{
public $version = null;
public $schemaVersion = null;
public $schemaDocumentation = null;
public $contentType = null;
public $cacheControl = null;
public $data;
public $errors = [];
/**
* @param $data
* @param $schemaVersion
* @param int $cacheLength
* @return OIntegrate_Response
*/
public static function create($data, $schemaVersion, $cacheLength = 300)
{
$response = new self();
$response->version = '1.0';
$response->data = $data;
$response->schemaVersion = $schemaVersion;
if ($cacheLength > 0) {
$response->cacheControl = "max-age=$cacheLength";
} else {
$response->cacheControl = "no-cache";
}
return $response;
}
/**
* @param $engine
* @param $output
* @param $templateLocation
*/
public function addTemplate($engine, $output, $templateLocation)
{
if (! array_key_exists('_template', $this->data)) {
$this->data['_template'] = [];
}
if (! array_key_exists($engine, $this->data['_template'])) {
$this->data['_template'][$engine] = [];
}
if (! array_key_exists($output, $this->data['_template'][$engine])) {
$this->data['_template'][$engine][$output] = [];
}
if (0 !== strpos($templateLocation, 'http')) {
$host = $_SERVER['HTTP_HOST'];
$proto = 'http';
$path = dirname($_SERVER['SCRIPT_NAME']);
$templateLocation = ltrim($templateLocation, '/');
$templateLocation = "$proto://$host$path/$templateLocation";
}
$this->data['_template'][$engine][$output][] = $templateLocation;
}
public function send()
{
header('OIntegrate-Version: 1.0');
header('OIntegrate-SchemaVersion: ' . $this->schemaVersion);
if ($this->schemaDocumentation) {
header('OIntegrate-SchemaDocumentation: ' . $this->schemaDocumentation);
}
header('Cache-Control: ' . $this->cacheControl);
$data = $this->data;
$data['_version'] = $this->schemaVersion;
$access = TikiLib::lib('access');
$access->output_serialized($data);
exit;
}
/**
* @param $engine
* @param $engineOutput
* @param $outputContext
* @param $templateFile
* @return mixed|string
*/
public function render($engine, $engineOutput, $outputContext, $templateFile)
{
$engine = OIntegrate::getEngine($engine, $engineOutput);
if (! $engine) {
$this->errors = [ 1000, tr('Engine "%0" not found.', $engineOutput) ];
return false;
}
if (! $output = OIntegrate::getConverter($engineOutput, $outputContext)) {
$this->errors = [ 1001, tr('Output converter "%0" not found.', $outputContext) ];
return false;
}
$raw = $engine->process($this->data, $templateFile);
return $output->convert($raw);
}
/**
* @param null $supportedPairs
* @return array
*/
public function getTemplates($supportedPairs = null)
{
if (! is_array($this->data) || ! isset($this->data['_template']) || ! is_array($this->data['_template'])) {
return [];
}
$templates = [];
foreach ($this->data['_template'] as $engine => $outputs) {
foreach ($outputs as $output => $files) {
if (is_array($supportedPairs) && ! in_array("$engine/$output", $supportedPairs)) {
continue;
}
$files = (array) $files;
foreach ($files as $file) {
$content = TikiLib::lib('tiki')->httprequest($file);
$templates[] = [
'engine' => $engine,
'output' => $output,
'content' => $content,
];
}
}
}
return $templates;
}
}
/**
*
*/
class OIntegrate_Engine_JavaScript implements OIntegrate_Engine
{
/**
* @param $data
* @param $templateFile
* @return string
*/
public function process($data, $templateFile)
{
$json = json_encode($data);
return <<<EOC
<script type="text/javascript">
var response = $json;
</script>
EOC
. file_get_contents($templateFile);
}
}
/**
*
*/
class OIntegrate_Engine_Smarty implements OIntegrate_Engine
{
private $changeDelimiters;
/**
* @param bool $changeDelimiters
*/
public function __construct($changeDelimiters = false)
{
$this->changeDelimiters = $changeDelimiters;
}
/**
* @param $data
* @param $templateFile
* @return mixed
*/
public function process($data, $templateFile)
{
/** @var Smarty_Tiki $smarty */
$smarty = new Smarty_Tiki();
$smarty->setTemplateDir(dirname($templateFile));
if ($this->changeDelimiters) {
$smarty->left_delimiter = '{{';
$smarty->right_delimiter = '}}';
}
$smarty->assign('response', $data);
return $smarty->fetch($templateFile);
}
}
/**
* Engine to pass on raw data and mapping info from the template
*/
class OIntegrate_Engine_Index implements OIntegrate_Engine
{
/**
* @param array $data
* @param string $templateFile
* @return array
*/
public function process($data, $templateFile)
{
$mappingString = file_get_contents($templateFile);
$mapping = json_decode($mappingString, true);
if ($mappingString && ! $mapping) {
Feedback::error(tr('Could decode JSON webservice template for indexing, check the syntax'));
}
return [
'data' => $data,
'mapping' => $mapping,
];
}
}
/**
*
*/
class OIntegrate_Converter_Direct implements OIntegrate_Converter // {{{
{
/**
* @param $content
* @return mixed
*/
public function convert($content)
{
return $content;
}
}
/**
*
*/
class OIntegrate_Converter_EncodeHtml implements OIntegrate_Converter // {{{
{
/**
* @param $content
* @return string
*/
public function convert($content)
{
return htmlentities($content, ENT_QUOTES, 'UTF-8');
}
}
/**
*
*/
class OIntegrate_Converter_HtmlToTiki implements OIntegrate_Converter // {{{
{
/**
* @param $content
* @return string
*/
public function convert($content)
{
return '~np~' . $content . '~/np~';
}
}
/**
*
*/
class OIntegrate_Converter_TikiToHtml implements OIntegrate_Converter // {{{
{
/**
* @param $content
* @return mixed|string
*/
public function convert($content)
{
return TikiLib::lib('parser')->parse_data(htmlentities($content, ENT_QUOTES, 'UTF-8'));
}
}
/**
* Attempt to index the result from the request
*/
class OIntegrate_Converter_Indexer implements OIntegrate_Converter
{
private $format;
public function __construct($format = 'none')
{
$this->format = $format;
}
/**
* @param $content
* @return mixed|string
*/
public function convert($content)
{
if ($this->format === 'html' || $this->format === 'tikiwiki') {
if (! empty($_REQUEST['nt_name'])) { // preview from admin/webservice page
$source = new Search_ContentSource_WebserviceSource();
$factory = new Search_Type_Factory_Direct();
if ($_REQUEST['nt_output'] === 'mindex') {
$documents = $source->getDocuments();
$data = [];
$count = 0;
foreach ($documents as $document) {
if (strpos($document, $_REQUEST['nt_name']) === 0) {
$data[$document] = $source->getDocument($document, $factory);
$count++;
if ($count > 100) { // enough for a preview?
break;
}
}
}
} else {
$data = $source->getDocument($_REQUEST['nt_name'], $factory);
}
$output = '<h3>' . tr('Parsed Data') . '</h3>';
$output .= '<pre style="max-height: 40em; overflow: auto; white-space: pre-wrap">';
$output .= htmlentities(
print_r($data, true),
ENT_QUOTES,
'UTF-8'
);
} else {
$output = '<h3>' . tr('Data') . '</h3>';
$output .= '<pre style="max-height: 20em; overflow: auto; white-space: pre-wrap">';
$output .= htmlentities(
json_encode($content['data'], JSON_PRETTY_PRINT),
ENT_QUOTES,
'UTF-8'
);
$output .= '</pre>';
if ($this->format === 'html') {
$output .= '<h3>' . tr('Mapping') . '</h3>';
$output .= '<pre style="max-height: 20em; overflow: auto; white-space: pre-wrap">';
$output .= htmlentities(
json_encode($content['mapping'], JSON_PRETTY_PRINT),
ENT_QUOTES,
'UTF-8'
);
$output .= '</pre>';
} else { // wiki mode from plugin
$output = "~np~{$output}~/np~";
}
}
return $output;
} else {
return $content;
}
}
}