commit ba0ddd7f3dd1489ff1d257cc0483c97abaf6d5ce
parent 9571fe349057d63c16d6f7a5c51da4f7d960acdb
Author: dankert <openrat@jandankert.de>
Date: Sun, 30 Jan 2022 23:38:42 +0100
Refactoring: Only 1 http-endpoint for both the UI and the API. Path "/api" is not available any more, all API data is served under "/".
Diffstat:
24 files changed, 711 insertions(+), 521 deletions(-)
diff --git a/api/index.php b/api/index.php
@@ -1,26 +0,0 @@
-<?php
-// Excecuting the CMS application programming interface (API)
-
-require('../modules/autoload.php');
-
-use cms\api\API;
-use cms\base\Startup;
-
-Startup::initialize();
-
-try {
- // Cookie-Path: Actual path without '/api'.
- define('COOKIE_PATH',substr(dirname($_SERVER['SCRIPT_NAME']),0,-3));
-
- API::execute();
-
-} catch (Exception $e) {
-
- if (!headers_sent())
- header('HTTP/1.0 500 Internal Server Error');
-
- echo $e->__toString();
-}
-
-
-?>
-\ No newline at end of file
diff --git a/api/web/index.php b/api/web/index.php
@@ -10,7 +10,7 @@
<p>A web interface for communicating with the CMS API.</p>
<hr>
-<form action="../../api/">
+<form action="../../">
<table>
<tr>
<th>Parameter</th><th>Value</th>
@@ -50,7 +50,7 @@
</select>
<select name="output">
- <?php foreach( array('HTML','JSON','XML','YAML') as $type ) { ?>
+ <?php foreach( array('JSON','XML','YAML','HTML','PLAIN',) as $type ) { ?>
<option value="<?php echo strtolower($type) ?>"><?php echo $type ?></option>
<?php } ?>
</select>
diff --git a/index.php b/index.php
@@ -3,77 +3,12 @@
require('modules/autoload.php');
use cms\base\Startup;
+use cms\output\OutputFactory;
use cms\ui\UI;
Startup::initialize();
-try {
- UI::execute();
-
-} catch (Exception $e) {
-
-if (!headers_sent())
-{
- header('HTTP/1.0 500 Internal Server Error');
- header('Content-Type: text/html; charset=UTF-8');
- header('Content-Security-Policy: style-src: inline; default: self');
-}
-
-?><!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1"/>
- <title>Service currently unavailable</title>
- <style type="text/css">
-
- header, main {
- display: block
- }
-
- body {
- width: 100%;
- height: 100%;
- background-color: rgba(13,8,5,0.58);
- color: white;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
- line-height: 1.4;
- font-size: 1.5em;
- text-align: center;
- }
-
- pre {
- margin:10%;
- width: 80%;
- overflow: visible;
- height: 40%;
- color: silver;
- text-align: left;
- font-size: 0.6rem;
- }
-
- h1 {
- font-size: 2em;
- }
-</style>
-</head>
-<body>
-
-<header>
- <h1>Sorry, our service is currently unavailable</h1>
-</header>
-
-<main>
-
- <p>Something went terribly wrong 😞</p>
-
- <pre><?php // Display exceptions only in development mode, because they may contain sensitive internal information like passwords.
- if (!defined('DEVELOPMENT') || DEVELOPMENT ) {
- echo $e->__toString(); }
- ?></pre>
-</main>
-
-</body>
-</html><?php
-
-}
+$output = OutputFactory::createOutput();
+header('X-CMS-Output-Type: ' . get_class($output ) );
+header('Content-Type: ' . $output->getContentType() . '; charset=' . Startup::CHARSET);
+$output->execute();
+\ No newline at end of file
diff --git a/modules/cms/action/object/ObjectDelaclAction.class.php b/modules/cms/action/object/ObjectDelaclAction.class.php
@@ -6,6 +6,7 @@ use cms\action\ObjectAction;
use cms\model\Permission;
use cms\model\BaseObject;
use language\Messages;
+use util\exception\SecurityException;
use util\Http;
class ObjectDelaclAction extends ObjectAction implements Method {
@@ -27,7 +28,7 @@ class ObjectDelaclAction extends ObjectAction implements Method {
$o->load();
if ( !$o->hasRight( Permission::ACL_GRANT ) )
- Http::notAuthorized('no grant rights'); // Da wollte uns wohl einer vereimern.
+ throw new SecurityException( 'no grant rights' ); // Da wollte uns wohl einer vereimern.
$permission->delete(); // Weg mit der ACL
diff --git a/modules/cms/api/API.class.php b/modules/cms/api/API.class.php
@@ -1,252 +0,0 @@
-<?php
-
-namespace cms\api;
-
-use BadMethodCallException;
-use cms\action\RequestParams;
-use cms\base\Startup;
-use cms\Dispatcher;
-use Exception;
-use util\Http;
-use logger\Logger;
-use \util\exception\ObjectNotFoundException;
-use util\exception\UIException;
-use util\exception\SecurityException;
-use util\json\JSON;
-use util\Session;
-use util\XML;
-use util\YAML;
-
-/**
- * Entrypoint for all API requests.
- */
-class API
-{
- const OUTPUT_PHPARRAY = 1;
- const OUTPUT_PHPSERIALIZE = 2;
- const OUTPUT_JSON = 3;
- const OUTPUT_XML = 4;
- const OUTPUT_YAML = 5;
- const OUTPUT_HTML = 6;
- const OUTPUT_PLAIN = 7;
-
-
- /**
- * Führt einen API-Request durch.
- */
- public static function execute()
- {
- $createDataWithError = function( $status, $message, $cause ) {
-
- Logger::warn($cause);
- API::sendHTTPStatus($status, $message);
-
- $data = [
- 'status' => $status,
- 'message' => $message
- ];
-
- // Traces only in DEVELOPMENT mode
- // for security reasons, because traces may contain sensitive information.
- if (!defined('DEVELOPMENT') || DEVELOPMENT)
- $data['cause'] = API::exceptionToArray($cause);
-
- return $data;
- };
-
- try {
- $request = new RequestParams();
-
- $dispatcher = new Dispatcher();
-
- $dispatcher->request = $request;
-
- $data = $dispatcher->doAction();
-
- } catch (BadMethodCallException $e) {
- $data = $createDataWithError( 204, 'Method not found' , $e );
- } catch (ObjectNotFoundException $e) {
- $data = $createDataWithError( 204, 'Object not found' , $e );
- } catch (UIException $e) {
- $data = $createDataWithError( 500, 'Internal CMS Error', $e );
- } catch (SecurityException $e) {
- $data = $createDataWithError( 403, 'Forbidden' , $e );
- } catch (Exception $e) {
- $data = $createDataWithError( 500, 'Internal Server Error', $e );
- }
-
-
- if ( Logger::isTraceEnabled() )
- Logger::trace('Output' . "\n" . print_r($data, true));
-
- // Weitere Variablen anreichern.
- $data['session'] = ['name' => session_name(), 'id' => session_id(), 'token' => Session::token()];
- $data['version'] = Startup::VERSION;
- $data['api' ] = Startup::API_LEVEL;
-
-
- switch (API::discoverOutputType()) {
-
- case self::OUTPUT_PHPARRAY:
- header('Content-Type: application/php-array; charset=UTF-8');
- $output = print_r($data, true);
- break;
-
- case self::OUTPUT_PLAIN:
- header('Content-Type: text/plain; charset=UTF-8');
- $output = print_r($data, true);
- break;
-
- case self::OUTPUT_PHPSERIALIZE:
- header('Content-Type: application/php-serialized; charset=UTF-8');
- $output = serialize($data);
- break;
-
- case self::OUTPUT_JSON:
- header('Content-Type: application/json; charset=UTF-8');
- $output = JSON::encode($data);
- break;
-
- case self::OUTPUT_XML:
- $xml = new XML();
- $xml->root = 'server'; // Name des XML-root-Elementes
- header('Content-Type: application/xml; charset=UTF-8');
- $output = $xml->encode($data);
- break;
-
- case self::OUTPUT_HTML:
- header('Content-Type: text/html; charset=UTF-8');
- $output = '<html><body><h1>API response:</h1><hr /><pre>';
- $output .= print_r($data,true);
- $output .= '</pre></body></html>';
- break;
-
- case self::OUTPUT_YAML:
- header('Content-Type: application/yaml; charset=UTF-8');
- $output = YAML::dump($data);
- break;
- }
-
-
- if (!headers_sent())
- // HTTP Spec:
- // "Applications SHOULD use this field to indicate the transfer-length of the
- // message-body, unless this is prohibited by the rules in section 4.4."
- //
- // And the overhead of 'Transfer-Encoding: chunked' is eliminated...
- header('Content-Length: ' . strlen($output));
-
- echo $output;
- }
-
- /**
- * Discovering the output-type for this API-request
- *
- * @return int constant of self::CMS_API_OUTPUT_*
- */
- private static function discoverOutputType()
- {
- $types = Http::getAccept();
-
- $reqOutput = strtolower(@$_REQUEST['output']);
-
- // First check: The output parameter has precedence over HTTP headers
- if ( $reqOutput == 'php-array')
- return self::OUTPUT_PHPARRAY;
-
- if ( $reqOutput == 'php')
- return self::OUTPUT_PHPSERIALIZE;
-
- if ( $reqOutput == 'json')
- return self::OUTPUT_JSON;
-
- if ( $reqOutput == 'xml')
- return self::OUTPUT_XML;
-
- if ( $reqOutput == 'yaml')
- return self::OUTPUT_YAML;
-
- // Lets check the HTTP request headers
- if (in_array('application/php-array', $types) )
- return self::OUTPUT_PHPARRAY;
-
- if (in_array('application/php-serialized', $types) )
- return self::OUTPUT_PHPSERIALIZE;
-
- if (in_array('application/json', $types) )
- return self::OUTPUT_JSON;
-
- if (in_array('application/xml', $types) )
- return self::OUTPUT_XML;
-
- if (in_array('application/yaml', $types) )
- return self::OUTPUT_YAML;
-
- if (in_array('text/html', $types))
- return self::OUTPUT_HTML; // normally an ordinary browser.
-
- return self::OUTPUT_PLAIN; // Fallback
- }
-
- /**
- * @param $status int HTTP-Status
- * @param $text string Statustext
- */
- private static function sendHTTPStatus($status, $text)
- {
- if (headers_sent()) {
- //echo "$status $text";
- ; // There is nothing we can do. Every output would destroy the JSON, XML, whatever.
- } else {
- header('HTTP/1.0 ' . intval($status) . ' ' . $text);
- }
- }
-
- /**
- * Converting an exception to an array.
- *
- * This will contain all exceptions out of the exception chain.
- *
- * @param $e Exception
- */
- private static function exceptionToArray($e)
- {
- $data = array(
- 'error'=>get_class($e),
- 'description'=>$e->getMessage(),
- 'code'=>$e->getCode(),
-
- 'trace'=>array_merge( array( array(
- 'file'=>$e->getFile(),
- 'line'=>$e->getLine(),
- 'function'=>'',
- 'class' => ''
- )), API::removeArgsFromTrace($e->getTrace()))
- );
-
- // the cause of the exception is another exception.
- if ( $e->getPrevious() )
- $data['cause'] = API::exceptionToArray($e->getPrevious() );
-
- return $data;
- }
-
-
- /**
- * Removing the call argument from the trace.
- *
- * This is because of security reasons. The arguments could be an information leak.
- *
- * @param $trace array
- * @return array
- */
- private static function removeArgsFromTrace($trace)
- {
- foreach( $trace as &$t )
- {
- unset($t['args']);
- }
-
- return $trace;
- }
-}
diff --git a/modules/cms/base/Startup.class.php b/modules/cms/base/Startup.class.php
@@ -27,6 +27,7 @@ class Startup {
const MIN_VERSION = '5.6'; // minimum required PHP version.
const API_LEVEL = '2'; // public API version.
+ const CHARSET = 'UTF-8'; // Everything is UTF-8.
const IMG_EXT = '.gif';
const IMG_ICON_EXT = '.png';
@@ -36,6 +37,8 @@ class Startup {
const THEMES_DIR = './modules/cms/ui/themes/';
const CSS_PREFIX = 'or-';
const DEFAULT_CONFIG_FILE = __DIR__ . '/../../../config/config.yml';
+ const APP_DIR = __DIR__ . '/../../../';
+ const MODULE_DIR = self::APP_DIR .'modules/';
/**
* This is the application name.
@@ -67,6 +70,9 @@ class Startup {
// in some situations we want to know, if the CMS is really started up.
define('APP_STARTED','1');
+
+ // Cookie path
+ define('COOKIE_PATH',dirname($_SERVER['SCRIPT_NAME']).'/');
}
protected static function checkPHPVersion()
diff --git a/modules/cms/output/APIOutput.class.php b/modules/cms/output/APIOutput.class.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace cms\output;
+
+use BadMethodCallException;
+use cms\action\RequestParams;
+use cms\api\API;
+use cms\base\Startup;
+use cms\Dispatcher;
+use Exception;
+use util\Http;
+use logger\Logger;
+use \util\exception\ObjectNotFoundException;
+use util\exception\UIException;
+use util\exception\SecurityException;
+use util\json\JSON;
+use util\Session;
+use util\XML;
+use util\YAML;
+
+/**
+ * Entrypoint for all API requests.
+ */
+abstract class APIOutput extends BaseOutput
+{
+ /**
+ * Converting an exception to an array.
+ *
+ * This will contain all exceptions out of the exception chain.
+ *
+ * @param $e Exception
+ */
+ private static function exceptionToArray($e)
+ {
+ $data = array(
+ 'error'=>get_class($e),
+ 'description'=>$e->getMessage(),
+ 'code'=>$e->getCode(),
+
+ 'trace'=>array_merge( array( array(
+ 'file'=>$e->getFile(),
+ 'line'=>$e->getLine(),
+ 'function'=>'',
+ 'class' => ''
+ )), self::removeArgsFromTrace($e->getTrace()))
+ );
+
+ // the cause of the exception is another exception.
+ if ( $e->getPrevious() )
+ $data['cause'] = self::exceptionToArray($e->getPrevious() );
+
+ return $data;
+ }
+
+ /**
+ * Removing the call argument from the trace.
+ *
+ * This is because of security reasons. The arguments could be an information leak.
+ *
+ * @param $trace array
+ * @return array
+ */
+ private static function removeArgsFromTrace($trace)
+ {
+ foreach( $trace as &$t )
+ {
+ unset($t['args']);
+ }
+
+ return $trace;
+ }
+
+ protected function outputData($request, $data)
+ {
+ $data += [
+ 'output' => $data,
+ 'session' => [
+ 'name' => session_name(),
+ 'id' => session_id(),
+ 'token' => Session::token()
+ ],
+ 'version' => Startup::VERSION,
+ 'api' => Startup::API_LEVEL,
+ ];
+
+ $output = $this->renderOutput( $data );
+
+ // HTTP Spec:
+ // "Applications SHOULD use this field to indicate the transfer-length of the
+ // message-body, unless this is prohibited by the rules in section 4.4."
+ //
+ // And the overhead of 'Transfer-Encoding: chunked' is eliminated...
+ header('Content-Length: ' . strlen($output));
+ echo $output;
+ }
+
+ abstract protected function renderOutput( $data );
+
+ protected function setError($text, $cause)
+ {
+ $data = [
+ 'message' => $text
+ ];
+
+ // Traces only in DEVELOPMENT mode
+ // for security reasons, because traces may contain sensitive information.
+ if (!defined('DEVELOPMENT') || DEVELOPMENT)
+ $data['cause'] = $this->exceptionToArray($cause);
+
+ $this->outputData($data);
+ }
+
+}
diff --git a/modules/cms/output/BaseOutput.class.php b/modules/cms/output/BaseOutput.class.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace cms\output;
+
+use BadMethodCallException;
+use cms\action\RequestParams;
+use cms\base\Language as L;
+use cms\base\Startup;
+use cms\Dispatcher;
+use Exception;
+use template_engine\engine\TemplateRunner;
+use util\Http;
+use logger\Logger;
+use LogicException;
+use \util\exception\ObjectNotFoundException;
+use util\exception\UIException;
+use util\exception\SecurityException;
+use template_engine\engine\TemplateEngine;
+use util\Session;
+use util\text\TextMessage;
+
+
+/**
+ * base class for output.
+ */
+abstract class BaseOutput implements Output
+{
+ abstract protected function outputData($request, $data);
+
+ /**
+ * Calling the dispatcher.
+ */
+ public function execute()
+ {
+ $request = new RequestParams();
+
+ $this->beforeAction( $request );
+
+ $dispatcher = new Dispatcher();
+ $dispatcher->request = $request;
+
+ try {
+ $data = $dispatcher->doAction();
+
+ $this->outputData( $request,$data );
+ } catch (BadMethodCallException $e) {
+ // Action-Method does not exist.
+ Logger::debug( $e );
+ Http::noContent();
+ $this->setError("Method not found",$e);
+ } catch (ObjectNotFoundException $e) {
+ Logger::debug( $e ); // only debug, because this may happen on links to deleted objects.
+ Http::noContent();
+ $this->setError("No content",$e);
+ } catch (UIException $e) {
+ Logger::warn( $e );
+ $this->setError(L::lang($e->key,$e->params),$e);
+ } catch (SecurityException $e) {
+ Logger::info($e);
+ Http::notAuthorized();
+ $this->setError("You are not allowed to execute this action.",$e);
+ } catch (Exception $e) {
+ Logger::warn( $e );
+ // Sorry, our service is currently unavailable
+ Http::serverError();
+ $this->setError("Internal CMS error",$e);
+ }
+ }
+
+ protected function beforeAction( $request )
+ {
+ }
+
+
+ abstract protected function setError($text, $cause);
+
+
+ protected function setStatus( $status, $text )
+ {
+ header('HTTP/1.0 ' . intval($status) . ' ' . $text);
+ }
+
+}
+\ No newline at end of file
diff --git a/modules/cms/output/HtmlOutput.class.php b/modules/cms/output/HtmlOutput.class.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace cms\output;
+
+use BadMethodCallException;
+use cms\action\RequestParams;
+use cms\base\Language as L;
+use cms\base\Startup;use cms\Dispatcher;
+use cms\output\BaseOutput;
+use Exception;
+use template_engine\engine\TemplateRunner;
+use util\Http;
+use logger\Logger;
+use LogicException;
+use \util\exception\ObjectNotFoundException;
+use util\exception\UIException;
+use util\exception\SecurityException;
+use template_engine\engine\TemplateEngine;
+use util\text\TextMessage;
+
+
+/**
+ * Executing the Openrat CMS User Interface.
+ */
+class HtmlOutput extends BaseOutput
+{
+ /**
+ * Shows the complete UI.
+ */
+ protected function beforeAction($request)
+ {
+ // Sending the Content-Security-Policy.
+ self::setContentSecurityPolicy();
+
+ if ( @$_REQUEST['scope']=='openid' ) {
+ $request->redirectActionAndMethod('login','oidc');
+ }
+ elseif (empty($request->action)) {
+ $request->redirectActionAndMethod('index','show' );
+ }
+
+ if ( $request->isAction )
+ throw new \RuntimeException('The UI does not accept POST requests');
+
+ if ( in_array( $request->action,['index','tree','title','usergroup']) )
+ $request->isUIAction = true;
+
+ }
+
+
+
+ protected function outputData($request, $data)
+ {
+ // The action is able to change its method and action name.
+ $subaction = $request->method;
+ $action = $request->action;
+
+
+ $this::outputTemplate($request,$action, $subaction, $data['output'] );
+ }
+
+
+ /**
+ * Executes and outputs a HTML template.
+ *
+ * @param $request RequestParams
+ * @param $action string action
+ * @param $subaction string method
+ * @param $outputData array Output data
+ */
+ private static function outputTemplate($request, $action, $subaction, $outputData)
+ {
+ $templateFile = Startup::MODULE_DIR . 'cms/ui/themes/default/html/views/' . $action.'/'.$subaction . '.php';
+
+ if ( DEVELOPMENT )
+ header('X-OR-Template: '.$templateFile);
+
+ $engine = new TemplateRunner();
+ //$engine->request = $request;
+ $engine->executeTemplate( $templateFile, $outputData );
+ }
+
+
+ /**
+ * Content-Security-Policy.
+ */
+ private static function setContentSecurityPolicy()
+ {
+ // config is not loaded yet. Allow nothing...
+ header('Content-Security-Policy: default-src \'none\'' );
+
+ // This will be overwritten by the index action
+ }
+
+
+ protected function setError($text, $cause)
+ {
+if (!headers_sent())
+{
+ header('HTTP/1.0 500 Internal Server Error');
+ header('Content-Security-Policy: style-src: inline; default: self');
+}
+
+?><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
+ <title><?php echo $text ?></title>
+ <style type="text/css">
+
+ header, main {
+ display: block
+ }
+
+ body {
+ width: 100%;
+ height: 100%;
+ background-color: rgba(13,8,5,0.58);
+ color: white;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ line-height: 1.4;
+ font-size: 1.5em;
+ text-align: center;
+ }
+
+ pre {
+ margin:10%;
+ width: 80%;
+ overflow: visible;
+ height: 40%;
+ color: silver;
+ text-align: left;
+ font-size: 0.6rem;
+ }
+
+ h1 {
+ font-size: 2em;
+ }
+</style>
+</head>
+<body>
+
+<header>
+ <h1><?php echo $text ?></h1>
+</header>
+
+<main>
+ <p>Something went terribly wrong 😞</p>
+
+ <pre><?php // Display exceptions only in development mode, because they may contain sensitive internal information like passwords.
+ if (!defined('DEVELOPMENT') || DEVELOPMENT ) {
+ echo $cause->__toString();
+ }
+ ?></pre>
+</main>
+
+</body>
+</html><?php
+
+ }
+
+ public function getContentType()
+ {
+ return 'text/html';
+ }
+}
diff --git a/modules/cms/output/HtmlPlainOutput.class.php b/modules/cms/output/HtmlPlainOutput.class.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+
+/**
+ * JSON Rendering.
+ */
+class HtmlPlainOutput extends APIOutput
+{
+ /**
+ * Renders the output in JSON Format.
+ */
+ protected function renderOutput( $data )
+ {
+ $output = '<html><body><h1>API response:</h1><hr /><pre>';
+ $output .= print_r($data,true);
+ $output .= '</pre></body></html>';
+
+ return $output;
+ }
+
+ public function getContentType()
+ {
+ return 'text/html';
+ }
+}
diff --git a/modules/cms/output/JsonOutput.class.php b/modules/cms/output/JsonOutput.class.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+
+/**
+ * JSON Rendering.
+ */
+class JsonOutput extends APIOutput
+{
+ /**
+ * Renders the output in JSON Format.
+ */
+ protected function renderOutput( $data )
+ {
+ return JSON::encode($data);
+ }
+
+ public function getContentType()
+ {
+ return 'application/json';
+ }
+}
diff --git a/modules/cms/output/Output.class.php b/modules/cms/output/Output.class.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace cms\output;
+
+use BadMethodCallException;
+use cms\action\RequestParams;
+use cms\base\Language as L;
+use cms\Dispatcher;
+use Exception;
+use template_engine\engine\TemplateRunner;
+use util\Http;
+use logger\Logger;
+use LogicException;
+use \util\exception\ObjectNotFoundException;
+use util\exception\UIException;
+use util\exception\SecurityException;
+use template_engine\engine\TemplateEngine;
+use util\text\TextMessage;
+
+
+/**
+ * Executing the Openrat CMS User Interface.
+ * The request is executed by a dispatcher and the output is displayed with a template.
+ */
+interface Output
+{
+ /**
+ * Rendering output.
+ */
+ public function execute();
+
+ public function getContentType();
+}
diff --git a/modules/cms/output/OutputFactory.class.php b/modules/cms/output/OutputFactory.class.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace cms\output;
+
+use util\Http;
+
+class OutputFactory {
+
+ const OUTPUT_PHPARRAY = 1;
+ const OUTPUT_PHPSERIALIZE = 2;
+ const OUTPUT_JSON = 3;
+ const OUTPUT_XML = 4;
+ const OUTPUT_YAML = 5;
+ const OUTPUT_HTML = 6;
+ const OUTPUT_PLAIN = 7;
+
+
+ /**
+ * Map 'output' request to output type.
+ */
+ const MAP_OUTPUT = [
+ 'php-array' => self::OUTPUT_PHPARRAY,
+ 'php' => self::OUTPUT_PHPSERIALIZE,
+ 'json' => self::OUTPUT_JSON,
+ 'xml' => self::OUTPUT_XML,
+ 'yaml' => self::OUTPUT_YAML,
+ 'plain' => self::OUTPUT_PLAIN
+ ];
+
+ /**
+ * Map Accept-Header to Output type.
+ */
+ const MAP_ACCEPT = [
+ 'application/php-array' => self::OUTPUT_PHPARRAY,
+ 'application/php-serialized' => self::OUTPUT_PHPSERIALIZE,
+ 'application/json' => self::OUTPUT_JSON,
+ 'application/xml' => self::OUTPUT_XML,
+ 'application/yaml' => self::OUTPUT_YAML,
+ 'text/html' => self::OUTPUT_HTML,
+ ];
+
+ public static function createOutput() {
+
+ switch ( self::discoverOutputType() ) {
+ case self::OUTPUT_PHPARRAY:
+ return new PHPArrayOutput();
+ case self::OUTPUT_PHPSERIALIZE:
+ return new PHPSerializeOutput();
+ case self::OUTPUT_JSON:
+ return new JsonOutput();
+ // case self::
+ // return new HtmlPlainOutput();
+ case self::OUTPUT_XML:
+ return new XmlOutput();
+ case self::OUTPUT_YAML:
+ return new YamlOutput();
+ case self::OUTPUT_PLAIN:
+ return new PlainOutput();
+ case self::OUTPUT_HTML:
+ default:
+ return new HtmlOutput();
+ }
+ }
+
+
+
+ /**
+ * Discovering the output-type for this request
+ *
+ * @return int constant of self::OUTPUT_*
+ */
+ private static function discoverOutputType()
+ {
+ $reqOutput = strtolower(@$_REQUEST['output']);
+
+ // Try 1: Checking the 'output' request parameter.
+ if ( $reqOutput && array_key_exists( $reqOutput, self::MAP_OUTPUT ) )
+ return self::MAP_OUTPUT[ $reqOutput ];
+
+ // Try 2: Lets check the HTTP request headers
+ foreach( Http::getAccept() as $acceptType )
+ if ( array_key_exists( $acceptType, self::MAP_ACCEPT ) )
+ return self::MAP_ACCEPT[ $acceptType ];
+
+ // Fallback
+ return self::OUTPUT_HTML;
+ }
+
+}
+\ No newline at end of file
diff --git a/modules/cms/output/PHPArrayOutput.class.php b/modules/cms/output/PHPArrayOutput.class.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+
+/**
+ * JSON Rendering.
+ */
+class PHPArrayOutput extends APIOutput
+{
+ /**
+ * Renders the output in JSON Format.
+ */
+ protected function renderOutput( $data )
+ {
+ header('Content-Type: application/json; charset=UTF-8');
+ return JSON::encode($data);
+ }
+
+ public function getContentType()
+ {
+ return 'application/php-array';
+ }
+}
diff --git a/modules/cms/output/PHPSerializeOutput.class.php b/modules/cms/output/PHPSerializeOutput.class.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+
+/**
+ * JSON Rendering.
+ */
+class PHPSerializeOutput extends APIOutput
+{
+ /**
+ * Renders the output in JSON Format.
+ */
+ protected function renderOutput( $data )
+ {
+ header('Content-Type: application/json; charset=UTF-8');
+ return serialize($data);
+ }
+
+ public function getContentType()
+ {
+ return 'application/php-serialized';
+ }
+}
diff --git a/modules/cms/output/PlainOutput.class.php b/modules/cms/output/PlainOutput.class.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+use util\YAML;
+
+/**
+ * Plain text rendering.
+ */
+class PlainOutput extends APIOutput
+{
+ /**
+ * Renders the output as plain text.
+ */
+ protected function renderOutput( $data )
+ {
+ //return YAML::dump($data);
+ return print_r($data);
+ }
+
+ public function getContentType()
+ {
+ return 'text/plain';
+ }
+}
diff --git a/modules/cms/output/XmlOutput.class.php b/modules/cms/output/XmlOutput.class.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+use util\XML;
+
+/**
+ * JSON Rendering.
+ */
+class XmlOutput extends APIOutput
+{
+ /**
+ * Renders the output in JSON Format.
+ */
+ protected function renderOutput( $data )
+ {
+ $xml = new XML();
+ $xml->root = 'server'; // Name des XML-root-Elementes
+ return $xml->encode($data);
+
+ }
+
+ public function getContentType()
+ {
+ return 'application/xml';
+ }
+}
diff --git a/modules/cms/output/YamlOutput.class.php b/modules/cms/output/YamlOutput.class.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace cms\output;
+
+use cms\output\APIOutput;
+use util\json\JSON;
+use util\YAML;
+
+/**
+ * YAML Rendering.
+ */
+class YamlOutput extends APIOutput
+{
+ /**
+ * Renders the output in YAML Format.
+ */
+ protected function renderOutput( $data )
+ {
+ return YAML::dump($data);
+ }
+
+ public function getContentType()
+ {
+ return 'application/yaml';
+ }
+}
diff --git a/modules/cms/ui/UI.class.php b/modules/cms/ui/UI.class.php
@@ -1,130 +0,0 @@
-<?php
-
-namespace cms\ui;
-
-use BadMethodCallException;
-use cms\action\RequestParams;
-use cms\base\Language as L;
-use cms\Dispatcher;
-use Exception;
-use template_engine\engine\TemplateRunner;
-use util\Http;
-use logger\Logger;
-use LogicException;
-use \util\exception\ObjectNotFoundException;
-use util\exception\UIException;
-use util\exception\SecurityException;
-use template_engine\engine\TemplateEngine;
-use util\text\TextMessage;
-
-
-/**
- * Executing the Openrat CMS User Interface.
- * The request is executed by a dispatcher and the output is displayed with a template.
- *
- * @package cms\ui
- */
-class UI
-{
- /**
- * Shows the complete UI.
- */
- public static function execute()
- {
- $request = new RequestParams();
-
- try
- {
- define('COOKIE_PATH',dirname($_SERVER['SCRIPT_NAME']).'/');
-
- // Everything is UTF-8.
- header('Content-Type: text/html; charset=UTF-8');
-
- // Sending the Content-Security-Policy.
- self::setContentSecurityPolicy();
-
- if ( @$_REQUEST['scope']=='openid' ) {
- $request->redirectActionAndMethod('login','oidc');
- }
- elseif (empty($request->action)) {
- $request->redirectActionAndMethod('index','show' );
- }
-
- if ( $request->isAction )
- throw new \RuntimeException('The UI does not accept POST requests');
-
- if ( in_array( $request->action,['index','tree','title','usergroup']) )
- $request->isUIAction = true;
-
- UI::executeAction($request);
-
- } catch (BadMethodCallException $e) {
- // Action-Method does not exist.
- Logger::debug( $e );
- Http::noContent();
- } catch (ObjectNotFoundException $e) {
- Logger::debug( $e ); // only debug, because this may happen on links to deleted objects.
- Http::noContent();
- } catch (UIException $e) {
- Logger::warn( $e );
- throw new LogicException(L::lang($e->key,$e->params),0, $e);
- } catch (SecurityException $e) {
- Logger::info($e);
- Http::notAuthorized("You are not allowed to execute this action.");
- } catch (Exception $e) {
- Logger::warn( $e );
- throw new LogicException("Internal CMS error",0, $e);
- }
- }
-
-
- private static function executeAction($request)
- {
- $dispatcher = new Dispatcher();
- $dispatcher->request = $request;
-
- $data = $dispatcher->doAction();
-
-
- // The action is able to change its method and action name.
- $subaction = $dispatcher->request->method;
- $action = $dispatcher->request->action;
-
- UI::outputTemplate($request,$action, $subaction, $data['output']);
- }
-
-
- /**
- * Executes and outputs a HTML template.
- *
- * @param $request RequestParams
- * @param $action string action
- * @param $subaction string method
- * @param $outputData array Output data
- */
- private static function outputTemplate($request, $action, $subaction, $outputData)
- {
- $templateFile = __DIR__ . '/themes/default/html/views/' . $action.'/'.$subaction . '.php';
-
- if ( DEVELOPMENT )
- header('X-OR-Template: '.$templateFile);
-
- $engine = new TemplateRunner();
- //$engine->request = $request;
- $engine->executeTemplate( $templateFile, $outputData );
- }
-
-
- /**
- * Content-Security-Policy.
- */
- private static function setContentSecurityPolicy()
- {
- // config is not loaded yet. Allow nothing...
- header('Content-Security-Policy: default-src \'none\'' );
-
- // This will be overwritten by the index action
- }
-
-
-}
diff --git a/modules/cms/ui/themes/default/script/openrat/api.js b/modules/cms/ui/themes/default/script/openrat/api.js
@@ -30,7 +30,7 @@ export default class Api {
let api = this;
return new Promise( (resolve, reject) => {
- let load = fetch( './api/', { 'method':'POST', body:formData } );
+ let load = fetch( './', { 'method':'POST', body:formData } );
load.then( response => {
if ( ! response.ok )
diff --git a/modules/cms/ui/themes/default/script/openrat/view.js b/modules/cms/ui/themes/default/script/openrat/view.js
@@ -150,12 +150,7 @@ export default class View {
*/
static createUrl(action,subaction,id,extraid={},api=false )
{
- let url = './';
-
- if ( api )
- url += 'api/';
-
- url += '?';
+ let url = './?';
if(action)
url += '&action='+action;
diff --git a/modules/cms/ui/themes/default/script/plugin/jquery-plugin-orSearch.js b/modules/cms/ui/themes/default/script/plugin/jquery-plugin-orSearch.js
@@ -50,7 +50,7 @@ export default function( options )
$('.or-search').addClass('search--is-active');
dropdownEl.addClass('search-result--is-active');
- let url = './api/?action='+settings.action+'&subaction='+settings.method+'&output=json&search='+searchArgument;
+ let url = './?action='+settings.action+'&subaction='+settings.method+'&output=json&search='+searchArgument;
let response = await fetch( url, {
method: 'GET',
headers: {
diff --git a/modules/util/Http.class.php b/modules/util/Http.class.php
@@ -390,8 +390,11 @@ class Http
*
* @param String $message Eigener Hinweistext
*/
- public static function serverError($message, $reason = '')
+ public static function serverError($message = '', $reason = '')
{
+ /*
+ try {
+
if (class_exists('util\Session')) {
$db = DB::get();
if (is_object($db))
@@ -400,8 +403,12 @@ class Http
if (class_exists('logger\Logger'))
Logger::warn($message . "\n" . $reason);
+ }
+ catch( \Exception $e ) {
+ //error_log( $e->__toString() );
+ }*/
- Http::sendStatus(501, 'Internal Server Error', $message, $reason);
+ self::sendStatus(501, 'Internal Server Error');
}
@@ -414,10 +421,9 @@ class Http
* @param String $text Text
* @param String $message Eigener Hinweistext
*/
- public static function notAuthorized($message = '')
+ public static function notAuthorized()
{
- Logger::warn("Security warning: $message");
- Http::sendStatus(403, 'Not authorized', $message);
+ Http::sendStatus(403, 'Not authorized');
}
@@ -427,12 +433,10 @@ class Http
* Diese Funktion erzeugt einen "HTTP 404 Not found" und das
* Skript wird beendet.
*
- * @param String $text Text
- * @param String $message Eigener Hinweistext
*/
- public static function notFound($text, $message)
+ public static function notFound()
{
- Http::sendStatus(404, 'Not found', $message);
+ Http::sendStatus(404, 'Not found');
}
@@ -443,8 +447,7 @@ class Http
*/
public static function noContent()
{
- header('HTTP/1.0 204 No Content');
- exit;
+ self::sendStatus(204,'No Content');
}
@@ -453,32 +456,15 @@ class Http
*
* @param Integer $status HTTP-Status (ganzzahlig) (Default: 501)
* @param String $text HTTP-Meldung (Default: 'Internal Server Error')
- * @param String $message Eigener Hinweistext (Default: leer)
- * @param String $reason Technischer Grund (Default: leer)
*/
- private static function sendStatus($status = 501, $text = 'Internal Server Error', $message = '', $reason = '')
+ private static function sendStatus($status = 501, $text = 'Internal Server Error')
{
if (headers_sent()) {
- echo "$status $text\n$message";
+ echo "$status $text";
exit;
}
header('HTTP/1.0 ' . intval($status) . ' ' . $text);
-
-
- $types = Http::getAccept();
-
- header('Content-Type: text/html');
- $message = htmlentities($message);
- $reason = htmlentities($reason);
- echo <<<HTML
-<h1>$text</h1>
-<p>$message</p>
-<pre><?php echo $reason; ?></pre>
-<?php
-
-HTML;
- exit;
}
diff --git a/openapi.yaml b/openapi.yaml
@@ -12,7 +12,7 @@ schemes:
- https
- http
paths:
- /api/:
+ /:
get:
tags:
- pet